/*-----------------------------------------------------------------------
**
** 	dco2.c
**
**	Oracle OCI 8 bindings for Python
***************************************************************************

Copyright (C) 2000, 2001, 2002 Zope Corporation, Fredericksburg, VA, USA.
All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

    o Redistributions of source code must retain the above copyright
      notice, this list of conditions, and the disclaimer that follows.

    o Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions, and the following disclaimer in
      the documentation and/or other materials provided with the
      distribution.

    o Neither the name of Zope Corporation nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.


  THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION AND CONTRIBUTORS *AS
  IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ZOPE
  CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  DAMAGE.

***************************************************************************
**
**
** A note on nomenclature.  Class names are CamelCased, where as class
** methods visible from Python are CamelCased_methods.  Internal functions
** are lesserCamelCased.
**
** $Id: dco2.c,v 1.136 2003/08/27 15:51:30 matt Exp $
**
** IMPORTANT:
**
** When fetching objects from Oracle, the cursor defines a "result" to
** be a list of ResultSet objects.  Each Result set holds a fixed buffer
** of data which is overwriten in the next fetch() call.  Fetch returns
** the SAME result set if this is a continuation of a previous fetch, but
** the result set values are overwritten with new data.
**
** The ResultSet provides sequence methods, so when you pull a specific
** item from the ResultSet, you get a ResultSetItem.  This ResultSetItem
** will cache its converted value from the type conversion functions,
** so even if the underlying data in the ResultSet changes, the ResultSet
** item will NOT appear to change (except for the str() function operator
** on the ResultSetItem, which will always show the underlying data in
** the ResultSet buffer that corresponds to the relative displacement
** of that item in the buffer; i.e. only the value() method of the
** ResultSetItem is cached.)
**
** Normally, this is not important, as all tabluar data has been copied
** out of the buffers by Python, BUT a few important caveats exist:
**
**    a)  If a string has been returned, and Python did not COPY the
**        string data, then the String object is going to get invalidated
**	  and is going to break, badly--possibly even causing a core
**        dump, since the conversion object doesn't increment the refcount
**        on its data source parent.  So, not only can it get changed, it
** 	  could just go away.
**
**    b)  LONG columns are variable width, and so the column variable
**	  is a LongFetch data type instead.  This needs to be re-initialized
**	  for each fetch.  Fortunately, you can only do one row at a time
**	  with LONGs.
**
**    c)  LOB locators, also being indirect pointers, must be copied
**	  carefully.  Currently, the conversion functions will copy the
** 	  old OCILobLocator pointer to the LobLocator object they return,
**	  and then allocate a new OCILobLocator pointer for the potential
**	  re-use of the ResultSet column.
**
**-----------------------------------------------------------------------
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
#include <sys/time.h>
#include <unistd.h>
#else
#define WIN32_LEAN_AND_MEAN
#define USEFILETIME
#include <winsock.h>
#endif
#include <fcntl.h>
#include <time.h>
#include <oci.h>
/* Next header is XID structure from Oracle */
#ifdef WIN32                  // Added my Maan Hamze 3/2007
#define USEOWNXID 1
#endif
#ifdef USEOWNXID
#define XIDDATASIZE 128         /* size in bytes */
#define MAXGTRIDSIZE    64      /* maximum size in bytes of gtrid */
#define MAXBQUALSIZE    64      /* maximum size in bytes of bqual */
struct xid_t {
    long        formatID;       /* format identifier */
    long        gtrid_length;   /* value from 1 through 64 */
    long        bqual_length;   /* value from 1 through 64 */
    char        data[XIDDATASIZE];
};
typedef struct xid_t XID;
#else
#include <xa.h>
#endif
#include <Python.h>
#include <pythread.h>

#ifndef PyObject_DEL
#define PyObject_DEL(x) free(x)
#endif

/* If the build script detected Oracle 9i, turn Oracle 8i definition on too */

#ifdef ORACLE9i
# ifndef ORACLE8i
#  define ORACLE8i
# endif
#endif

/* Turn on object mode by default (largely unused) */

#if (defined(ORACLE8i) && !defined(NOOBJECT))
#define USEOBJECT
#endif

/* Turn off zero length conversion to NULL by default */
#ifndef ZEROLENISNULL
#define ZEROLENISNULL 0
#endif

/* Get around unused warnings with GCC -Wall by using this on a def for
** intentionally unused items.   Currently "ID" and "Traceprint" are
** meant to be unused; Traceprint is for debugging.
*/

#if (defined(__GNUC__) && defined(linux))
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

#define PUBLIC

#if (PY_VERSION_HEX <= 0x010502F0)
#define PyObject_Del(x) PyMem_Free((char *)x)
#endif

#define OBJECT(o) ((PyObject *) o)
#define MAX_RESULT_SET_COUNT 1000
#define MAX_BIND_COUNT 256


#ifdef DCO2MEMTRACE
#define PYMALLOC(x) dco2_pymem_malloc(x)
#define PYREALLOC(x,y) dco2_pymem_realloc(x,y)
#define PYFREE(x) dco2_pymem_free(x)
#else
#define PYMALLOC(x) PyMem_Malloc(x)
#define PYREALLOC(x,y) PyMem_Realloc(x,y)
#define PYFREE(x) PyMem_Free(x)
#endif


/*
** You may uncomment the line below if you have problems with Solaris
** installation (there are reports about problems with Oracle10g)
*/
//typedef unsigned int dword;


/*
** Native Numeric mode requires connecting with OCI Object mode enabled.
** It uses OCINumber calls to manipulate numbers as SQLT_VNU types.  Input
** binds are still bound as INT, FLOAT, or STRING (based on if they are
** INT, FLOAT, or LONG python types) but binding arrays created as type
** SQLT_VNU will convert their input parameters to SQLT_VNU.  With Native
** numerics, output SQLT_NUM results will be requested as SQLT_VNU as well,
** with Native Numeric off, output SQLT_NUM will be retrieved as SQLT_STR.
*/
#define NATIVENUMERIC 1

/* Set STRICT_SCALE=1 to make scale=0 precision=0 return floats, otheriwse
 * it will return INTs
 */

#define STRICT_SCALE 0

/*
** Number format string for Oracle
*/

/*                          1         2         3         4
**                  12345678901234567890123456789012345678901234567890 */
#define NUMFORMAT  "9999999999999999999999999999999999999999D999999999"
#define NUMFORMATD "9999999999999999999999999999999999999999"

/* TRACE defines (trace globals are elsewhere)
**
** The trace table (if it exists) is updated when the traceFlags global
** embodies all of the conditions set; the trace table contains entries
** of up to 7 words as well as a timestamp and descriptors of what those
** words mean.  The trace table can later be retrieved for analysis in
** case of problems
*/

#define TRACE(condition,args) if ((traceFlags & condition) == condition) *(trace args) = (char) condition, Traceprint1(traceLog, -1)

#define T_TSWITCH (0x08)
#define T_ENTRY   (0x11)
#define T_EXIT    (0x12)
#define T_ERROR   (0x14)
#define T_CALL    (0x21)
#define T_RETURN  (0x22)
#define T_OERROR  (0x24)
#define T_HCALL   (0xA1)
#define T_HRETURN (0xA2)
#define T_ARGS    (0x41)
#define T_RESULT  (0x42)
#define T_PROGRAM (0x10)
#define T_ORACLE  (0x20)
#define T_INFO    (0x40)
#define T_VERBOSE (0x80)
#define T_MALLOC  (0x91)
#define T_FREE    (0x92)
#define T_REALLOC (0x93)

/* Do doubleword alignment on ResultSet columns */
#define PADSIZE 8

#define SETLOBTYPE(t, n, i) if (t == SQLT_BLOB || t == SQLT_CLOB) { \
	n = "OCI_DTYPE_LOB"; \
	i = OCI_DTYPE_LOB; \
	} else { \
	n = "OCI_DTYPE_FILE"; \
	i = OCI_DTYPE_FILE; \
	}


/*-----------------------------------------------------------------------
**
** Data types
**
**-----------------------------------------------------------------------
*/

/*
** The Transaction object represents the transaction ID that is associated
** with a service context.
*/

typedef struct {
	PyObject_HEAD
	OCITrans	*tranp;			/* Transaction ID 	*/
	OCIError	*errhp;			/* Error handle		*/
	int		flags;			/* Flags		*/
	PyObject	*name;			/* Transaction Name	*/
	XID		xid;			/* XID			*/
} TransactionContext;

staticforward PyTypeObject TransactionContextType;

#define TC_XID (1<<0)
#define TC_NAME (1<<1)

/*
** The Server Context describes the various OCI handles that are used to
** represent a session
**
** For future use, this could be split into two components, as the user &
** service contexts can be multiplexed over a server handle.
*/

typedef struct {
	PyObject_HEAD
	OCIEnv		*envhp;			/* Environment handle 	*/
	OCIError	*errhp;			/* Error handle		*/
	OCIServer	*srvhp;			/* Server handle	*/
	OCISession	*usrhp;			/* User handle		*/
	OCISvcCtx	*svchp;			/* Service Context	*/
	TransactionContext *tctx;		/* Transaction Context  */
} ServerContext;

staticforward PyTypeObject ServerContextType;


/*
** Cursors can have lots of bindings...
*/

typedef struct {
	OCIBind	*bindp;				/* Binding pointer	*/
	PyObject *object;			/* Bound object		*/
	dvoid	*valuep;			/* Value pointer	*/
	sb4	valuesz;			/* Value size		*/
	ub2	dty;				/* Datatype		*/
	sb2	ind;				/* Indicator		*/
	ub2	alen;				/* Array length		*/
	ub2	rcode;				/* Result code		*/
	int	flags;				/* Binding flags	*/
	int	pieces;				/* # of pieces		*/
	union	{
		int	i;			/* in place storage	*/
		unsigned int ui;
		long	l;
		unsigned long ul;
		double	d;
		char	c[4];
		char	*s;
		void	*p;
	} u;
} Binding;

#define BIND_ARRAY_COLUMN	(1<<4)
#define BIND_IN_COLUMN		(1<<5)
#define BIND_CURSOR		(1<<6)

/* NOTE:
**
** CURSOR bindings (e.g. REF CURSOR) are currently bound by an existing
** Cursor object, but probably should dynamically allocate new cursors
** if that's what the implicit describe reveals.  This is annoying, however
** as it means there will be the creation of many cursors which will not
** be used (e.g. resultset size = 200)
**
** This could be addressed by detecting the SQLT_RSET in the implcit describe
** and forcing the resultset size to one.
**
** e.g.
**
**	Cursor.bindbypos(1, Cursor2)
**
** will bind cursor2 in the statement referenceed by Cursor, but will not
** create Cursor2
*/


/* The binding array is used for RETURNING...INTO */

typedef struct {
	dvoid	*valuep;			/* value ARRAY		*/
	sb4	*valueszp;			/* size ARRAY		*/
	ub2	*dtyp;				/* datatype ARRAY	*/
	sb2	*indp;				/* indicator ARRAY	*/
	ub2	*alenp;				/* Answer length ARRAY	*/
	ub2	*rcodep;			/* Result length ARRAY	*/
	PyObject **objectp;			/* Result object ARRAY  */
	long	entries;			/* For the realloc	*/
	long	count;				/* number used		*/
	long	itemsize;			/* For the realloc	*/
} BindingArray;

#define DEFAULT_BINDARRAY_SIZE 100

/*
**BindOut object
*/

typedef struct {
	PyObject_HEAD
	BindingArray	*ba;			/* Binding Array	*/
	ServerContext	*sc;			/* Server context	*/
	int		flags;			/* Flags		*/
	int		scale;			/* Numeric scale	*/
	int		precision;		/* Numeric precision	*/
	sb2		dty;			/* datatype		*/
} BindingArrayObject;

#define BAO_STATIC (0x01)

staticforward PyTypeObject BindingArrayObjectType;

/*
** BindingArrays can be pointed to by Bindings; they will be the pointer
** of the Binding Array static data union (u.p).  The flag BIND_ARRAY_COLUMN
** will be turned on for Binding Arrays.
*/

/*
** Cursor
**
*/

typedef struct {
	PyObject_HEAD
	ServerContext	*sc;			/* Server Context	*/
	OCIError	*errhp;			/* Error handle		*/
	PyObject	*definition;		/* Definition list	*/
	PyObject	*results;		/* Result set list	*/
	OCIStmt		*stmtp;			/* Statement pointer	*/
	ub2		type;			/* Statement type	*/
	char		flags;			/* Flags		*/
	char		longcol;		/* Long column		*/
	int		batchsz;		/* Batch size		*/
	int		count;			/* Current count	*/
	int		batchct;		/* Batch count		*/
	int		current;		/* Current result or -1	*/
	Binding		bind[MAX_BIND_COUNT];	/* Bindings		*/
} Cursor;

/* Cursor flags */
#define LONG_COLUMN	(1<<0)
#define LOB_COLUMN	(1<<1)
#define NO_RESULT	(1<<2)
#define CURSOR_INSPECT  (1<<3)
#define CURSOR_NAMEBIND_NEEDSFLUSH (1<<4)

staticforward PyTypeObject CursorType;

/*
** Lob Locators
*/

typedef struct {
	PyObject_HEAD
	ServerContext	*sc;			/* Server Context	*/
	OCIError	*errhp;			/* Error handle		*/
	OCILobLocator	*lobp;			/* LOB Locator		*/
	ub2		dty;			/* Type of locator	*/
	sb2		ind;			/* Indicator		*/
} LobLocator;

staticforward PyTypeObject LobLocatorType;

/*
** Long fetch holder
*/

typedef struct {
	char 	**data;				/* Data Buckets		*/
	long	*size;				/* Size of data 	*/
	long	*length;			/* Length of data	*/
	long	volume;				/* Volume of all pieces */
	long	buckets;			/* Number of buckets	*/
	int	pieces;				/* Pieces fetched	*/
	sb2	ind;
	ub2	rcode;
} LongFetch;

#define FETCHBUCKETS 	256
#define FETCHBUCKETSIZE 65536

/*
** Result Object
*/

typedef struct {
	PyObject_HEAD
	Cursor		*cursor;		/* Cursor		*/
	PyObject	*definition;		/* Definition tuple	*/
	OCIDefine	*defnp;			/* Definition pointer	*/
	ub2		column;			/* Result column	*/
	int		size;			/* Size of set		*/
	int		width;			/* Width of set		*/
	char		*modified;		/* Modification array	*/
	dvoid		*valuep;		/* Result array		*/
	sb4		valuesz;		/* Value size		*/
	ub2		dty;			/* Oracle type		*/
	ub2		cdty;			/* Conversion type	*/
	sb2		*indp;			/* Indicator array	*/
	ub2		*rlenp;			/* Length array		*/
	ub2		*rcodep;		/* Column result codes	*/
	ub2		fetchResultCode; 	/* Fetch result		*/
} ResultSet;

staticforward PyTypeObject ResultSetType;

/*
** ResultSetItem
*/

typedef struct {
	PyObject_HEAD
	ResultSet	*rs;			/* Result set		*/
	int		item;			/* Item			*/
	PyObject 	*pyItem;		/* Converted item	*/
} ResultSetItem;

staticforward PyTypeObject ResultSetItemType;

/*
** OracleDate
*/

typedef struct {
	PyObject_HEAD
	char		ocidate[7];		/* Oracle raw data	*/
	struct tm	tm;
	long		ticks;			/* seconds since epoch	*/
	PyObject	*str;			/* String format	*/
	PyObject	*repr;			/* Repr format		*/
} OracleDate;

staticforward PyTypeObject OracleDateType;

/*
** Row ID Descriptor
*/

typedef struct {
	PyObject_HEAD
	OCIRowid *rowid;			/* Row ID		*/
	ServerContext *sc;			/* Server Context	*/
} OracleRowID;

staticforward PyTypeObject OracleRowIDType;


/*
** traceRecord
**
*/

typedef struct {
	struct timeval  tv;
	unsigned char	code;
	char		fmt[7];
	union {
		int	i;
		char	*s;
	} args[7];
} traceRecord;

#define TRACECOUNT 512

unsigned int traceFlags = 0xFF;
unsigned int traceCount = 0;
unsigned int traceNext  = 0;
char *traceDump = NULL;
FILE *traceLog = NULL;
traceRecord *traceBase = NULL;

/*
** Descriptor Tables
*/

typedef struct {
	char	*attribute;
	ub4	attr;
	ub4	rtype;
	ub4	rsize;
	void	*proc;
} DescriptorEntry;

static PyObject *ErrorObject = NULL;
static PyObject *ProgrammingErrorObject = NULL;

#ifdef COMMONENVIRONMENT
static OCIEnv *GLBenvhp;
static int     GLBenvs = 0;		/* Try to work a fix	*/
#endif

UNUSED static char ID[]="$Id: dco2.c,v 1.136 2003/08/27 15:51:30 matt Exp $";
const static char NULLSTR[]="";		/* for null string bind	*/


/*-----------------------------------------------------------------------
**
** Prototypes
**
**-----------------------------------------------------------------------
*/

#define CONVERTOUTF(x) static PyObject *convertOut_##x(int type, int len, \
	int precision, int scale, void *data, ServerContext *sc)
#define CONVERTINF(x) static PyObject *convertIn_##x(int type, int len, \
	int precision, int scale, void *data, PyObject *obj, ServerContext *sc)

static PyObject *RaiseOCIError1(dvoid *hp, ub4 handle_type, int setpy);
static PyObject *Connect(PyObject *self, PyObject *args);
static void ServerContext_dealloc(ServerContext *self);
static PyObject *ServerContext_getattr(ServerContext *self, char *name);
static PyObject *ServerContext_close(ServerContext *self, PyObject *args);
static PyObject *ServerContext_cursor(ServerContext *self, PyObject *args);
static PyObject *ServerContext_commit(ServerContext *self, PyObject *args);
static PyObject *ServerContext_prepare(ServerContext *self, PyObject *args);
static PyObject *ServerContext_rollback(ServerContext *self, PyObject *args);
static PyObject *ServerContext_describe(ServerContext *self, PyObject *args);
static PyObject *ServerContext_rowid(ServerContext *self, PyObject *args);
static PyObject *ServerContext_getTransaction(ServerContext *self,
	PyObject *args);
static PyObject *ServerContext_newTransaction(ServerContext *self,
	PyObject *args);
static PyObject *ServerContext_setTransaction(ServerContext *self,
	PyObject *args);
static void Cursor_dealloc(Cursor *self);
static PyObject *Cursor_getattr(Cursor *self, char *name);
static PyObject *Cursor_close(Cursor *self, PyObject *args);
static PyObject *Cursor_prepare(Cursor *self, PyObject *args);
static int bindObject(Binding *bind, PyObject *args, int ctype);
static PyObject *Cursor_bind(Cursor *self, PyObject *obj, int pos,
	PyObject *ctypeo, char *name, int namelen);
static PyObject *Cursor_bindbypos(Cursor *self, PyObject *args);
static PyObject *Cursor_bindbyname(Cursor *self, PyObject *args);
static PyObject *Cursor_execute(Cursor *self, PyObject *args);
static PyObject *Cursor_describe(Cursor *self, PyObject *args);
static PyObject *Cursor_ResultSet(Cursor *self, int count);
static PyObject *Cursor_fetch(Cursor *self, PyObject *args);
static PyObject *Cursor_setPrefetch(Cursor *self, PyObject *args);
static PyObject *Cursor_rowcount(Cursor *self, PyObject *args);
static PyObject *LobLocator_alloc(ServerContext *self, PyObject *args);
static PyObject *ResultSet_alloc(Cursor *cursor, int column, int count);
static int typeByName(char *typename);
static int typeByType(int type);
static PyObject *TypeTable_lookup(PyObject *self, PyObject *args);
static void ResultSet_dealloc(ResultSet *self);
static PyObject *ResultSet_getattr(ResultSet *self, char *name);
static int ResultSet_length(ResultSet *self);
static PyObject *ResultSet_concat(ResultSet *self, PyObject *other);
static PyObject *ResultSet_repeat(ResultSet *self, int count);
static PyObject *ResultSet_item(ResultSet *self, int item);
static PyObject *ResultSet_slice(ResultSet *self, int start, int end);
static int ResultSet_ass_item(ResultSet *self, int start, PyObject *other);
static PyObject *ResultSet_ass_slice(ResultSet *self, int start, int end,
	PyObject *other);
static void ResultSetItem_dealloc(ResultSetItem *self);
static PyObject *ResultSetItem_getattr(ResultSetItem *self, char *name);
static PyObject *ResultSetItem_str(ResultSetItem *self);
static PyObject *ResultSetItem_value(ResultSetItem *self, PyObject *args);
static void LobLocator_dealloc(LobLocator *self);
static PyObject *LobLocator_getattr(LobLocator *self, char *name);
static PyObject *LobLocator_length(LobLocator *self, PyObject *args);
static PyObject *LobLocator_read(LobLocator *self, PyObject *args);
static PyObject *LobLocator_write(LobLocator *self, PyObject *args);
static PyObject *LobLocator_trim(LobLocator *self, PyObject *args);
static PyObject *LobLocator_open(LobLocator *self, PyObject *args);
static PyObject *LobLocator_close(LobLocator *self, PyObject *args);
static PyObject *LobLocator_getFileName(LobLocator *self, PyObject *args);
static PyObject *LobLocator_setFileName(LobLocator *self, PyObject *args);
static PyObject *LobLocator_fileExists(LobLocator *self, PyObject *args);
static TransactionContext *TransactionContext_alloc(ServerContext *sc,
		int makeHandle);
static PyObject *TransactionContext_getattr(TransactionContext *self,
	char *name);
static PyObject *TransactionContext_setName(TransactionContext *self,
		PyObject *args);
static PyObject *TransactionContext_setXID(TransactionContext *self,
		PyObject *args);
static PyObject *TransactionContext_flushCache(TransactionContext *self,
		PyObject *args);
static void TransactionContext_dealloc(TransactionContext *self);
static PyObject *OracleDate_alloc(PyObject *self, PyObject *args);
static PyObject *OracleDate2_alloc(PyObject *self, PyObject *args);
static void OracleDate_dealloc(OracleDate *self);
static PyObject *OracleDate_getattr(OracleDate *self, char *name);
static PyObject *OracleDate_str(OracleDate *self);
static PyObject *OracleDate_repr(OracleDate *self);
static int OracleDate_cmp(OracleDate *left, OracleDate *right);
static int OracleDate_coerce(PyObject **o1, PyObject **o2);
static PyObject *OracleDate_int(OracleDate *self);
static PyObject *OracleDate_long(OracleDate *self);
static PyObject *OracleDate_float(OracleDate *self);
static PyObject *OracleRowID_alloc(ServerContext *sc);
static void OracleRowID_dealloc(OracleRowID *self);
static int OracleRowID_cmp(OracleRowID *left, OracleRowID *right);
static PyObject *OracleRowID_str(OracleRowID *self);
static PyObject *OracleRowID_repr(OracleRowID *self);
static int longFetchInit(LongFetch *lf);
static char *longFetchAlloc(LongFetch *lf, long size);
static void longFetchRelease(LongFetch *lf);
static BindingArray *BindingArray_alloc(long elements, long itemsize);
static BindingArray *BindingArray_realloc(BindingArray *b, long elements);
static void BindingArray_dealloc(BindingArray *b);
static PyObject *ServerContext_BindingArrayObject(ServerContext *self,
	PyObject *args);
static void BindingArrayObject_dealloc(BindingArrayObject *obj);
static PyObject *BindingArrayObject_setStatic(BindingArrayObject *obj,
	PyObject *args);
static PyObject *BindingArrayObject_type(BindingArrayObject *obj,
	PyObject *args);
static PyObject *BindingArrayObject_width(BindingArrayObject *obj,
	PyObject *args);
static PyObject *BindingArrayObject_getattr(BindingArrayObject *obj,
	char *name);
static int BindingArrayObject_length(BindingArrayObject *obj);
static PyObject *BindingArrayObject_item(BindingArrayObject *obj, int item);
static int BindingArrayObject_ass_item(BindingArrayObject *obj, int item, PyObject *value);

static PyObject *listDescriptors(ServerContext *self, OCIParam *parmh,
	DescriptorEntry *entry, PyObject *ptr);
static PyObject *numberConversion(char *number);

static char *trace(char *fmt, ...);
static PyObject *Traceback(PyObject *self, PyObject *args);
static void Traceprint1(FILE *file, int count);
static void Tracedump1(void);

#ifdef DCO2MEMTRACE
static void *dco2_pymem_malloc(unsigned int size);
static void *dco2_pymem_realloc(void *ptr, unsigned int size);
static void dco2_pymem_free(void *ptr);
#endif

CONVERTOUTF(SQLT_CHR);
CONVERTOUTF(SQLT_STR);
CONVERTOUTF(SQLT_BIN);
CONVERTOUTF(SQLT_AFC);
CONVERTOUTF(SQLT_NUM);
CONVERTOUTF(SQLT_VNU);
CONVERTOUTF(SQLT_INT);
CONVERTOUTF(SQLT_FLT);
CONVERTOUTF(SQLT_DAT);
CONVERTOUTF(SQLT_TIMESTAMP);  // Added by Maan Hamze to support TIMESTAMP Datatype (3/1/2007)
CONVERTOUTF(SQLT_LNG);
CONVERTOUTF(SQLT_LBI);
CONVERTOUTF(SQLT_BLOB);
CONVERTOUTF(SQLT_CLOB);
CONVERTOUTF(SQLT_BFILE);
CONVERTOUTF(SQLT_CFILE);
CONVERTOUTF(SQLT_RDD);
CONVERTOUTF(SQLT_CUR);
CONVERTOUTF(SQLT_RSET);

#ifdef WIN32
static void gettimeofday(struct timeval *tz, void *zone);
#endif

/*-----------------------------------------------------------------------
**
** Constants
**
**-----------------------------------------------------------------------
*/

/* Docstrings */

static char ServerContextType__doc__[] =
	"An Oracle 8 database connection";

static char TransactionContextType__doc__[] =
	"An Oracle 8 transaction context";

static char LobLocatorType__doc__[] =
	"An Oracle 8 LOB locator";

static char CursorType__doc__[] =
	"An Oracle 8 cursor";

static char ResultSetType__doc__[] =
	"An Oracle 8 Result Set";

static char ResultSetItemType__doc__[] =
	"An Oracle 8 Result Set Item";

static char OracleDateType__doc__[] =
	"An Oracle 8 Date";

static char OracleRowIDType__doc__[] =
	"An Oracle 8 Row ID";

static char BindingArrayObjectType__doc__[] =
	"An Oracle 8 Output binding object";

/* Types */

static PyTypeObject ServerContextType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"ServerContext",			/* tp_name		*/
	sizeof(ServerContext),			/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) ServerContext_dealloc,	/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) ServerContext_getattr,	/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) NULL,				/* tp_compare		*/
	(reprfunc) NULL,			/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	NULL,					/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) NULL,			/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	ServerContextType__doc__		/* docstring		*/

};

static PyTypeObject TransactionContextType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"TransactionContext",			/* tp_name		*/
	sizeof(TransactionContext),		/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) TransactionContext_dealloc,/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) TransactionContext_getattr,/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) NULL,				/* tp_compare		*/
	(reprfunc) NULL,			/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	NULL,					/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) NULL,			/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	TransactionContextType__doc__		/* docstring		*/

};

static PyTypeObject LobLocatorType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"LobLocator",				/* tp_name		*/
	sizeof(LobLocator),			/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) LobLocator_dealloc,	/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) LobLocator_getattr,	/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) NULL,				/* tp_compare		*/
	(reprfunc) NULL,			/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	NULL,					/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) NULL,			/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	LobLocatorType__doc__			/* docstring		*/

};

static PySequenceMethods BindingArrayObjectSequenceMethods = {
	(inquiry) BindingArrayObject_length,	/* sq_length		*/
	(binaryfunc) NULL,			/* sq_concat		*/
	(intargfunc) NULL,			/* sq_repeat		*/
	(intargfunc) BindingArrayObject_item,	/* sq_item		*/
	(intintargfunc) NULL,			/* sq_slice		*/
	(intobjargproc) BindingArrayObject_ass_item,/* sq_ass_item	*/
	(intintobjargproc) NULL			/* sq_ass_slice		*/
};


static PyTypeObject BindingArrayObjectType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"BindingArray",				/* tp_name		*/
	sizeof(BindingArrayObject),		/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) BindingArrayObject_dealloc,/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) BindingArrayObject_getattr,/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) NULL,				/* tp_compare		*/
	(reprfunc) NULL,			/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	&BindingArrayObjectSequenceMethods,	/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) NULL,			/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	BindingArrayObjectType__doc__		/* docstring		*/

};

static PyTypeObject CursorType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"Cursor",				/* tp_name		*/
	sizeof(Cursor),				/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) Cursor_dealloc,		/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) Cursor_getattr,		/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) NULL,				/* tp_compare		*/
	(reprfunc) NULL,			/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	NULL,					/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) NULL,			/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	CursorType__doc__			/* docstring		*/

};

static PySequenceMethods ResultSetSequenceMethods = {
	(inquiry) ResultSet_length,		/* sq_length		*/
	(binaryfunc) ResultSet_concat,		/* sq_concat		*/
	(intargfunc) ResultSet_repeat,		/* sq_repeat		*/
	(intargfunc) ResultSet_item,		/* sq_item		*/
	(intintargfunc) ResultSet_slice,	/* sq_slice		*/
	(intobjargproc) ResultSet_ass_item,	/* sq_ass_item		*/
	(intintobjargproc) ResultSet_ass_slice	/* sq_ass_slice		*/
};

static PyTypeObject ResultSetType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"ResultSet",				/* tp_name		*/
	sizeof(ResultSet),			/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) ResultSet_dealloc,		/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) ResultSet_getattr,	/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) NULL,				/* tp_compare		*/
	(reprfunc) NULL,			/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	&ResultSetSequenceMethods,		/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) NULL,			/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	ResultSetType__doc__			/* docstring		*/

};

static PyTypeObject ResultSetItemType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"ResultSetItem",			/* tp_name		*/
	sizeof(ResultSetItem),			/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) ResultSetItem_dealloc,	/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) ResultSetItem_getattr,	/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) NULL,				/* tp_compare		*/
	(reprfunc) NULL,			/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	NULL,					/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) ResultSetItem_str,		/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	ResultSetItemType__doc__		/* docstring		*/

};

static PyNumberMethods OracleDateNumberMethods = {
	NULL,					/* nb_add		*/
	NULL,					/* nb_subtract		*/
	NULL,					/* nb_multiply		*/
	NULL,					/* nb_divide		*/
	NULL,					/* nb_remainder		*/
	NULL,					/* nb_divmod		*/
	NULL,					/* nb_power		*/
	NULL,					/* nb_negative		*/
	NULL,					/* nb_positive		*/
	NULL,					/* nb_absolute		*/
	NULL,					/* nb_nonzero		*/
	NULL,					/* nb_invert		*/
	NULL,					/* nb_lshift		*/
	NULL,					/* nb_rshift		*/
	NULL,					/* nb_and		*/
	NULL,					/* nb_xor		*/
	NULL,					/* nb_or		*/
	(coercion) OracleDate_coerce,		/* nb_coerce		*/
	(unaryfunc) OracleDate_int,		/* nb_int		*/
	(unaryfunc) OracleDate_long,		/* nb_long		*/
	(unaryfunc) OracleDate_float,		/* nb_float		*/
	NULL,					/* nb_oct		*/
	NULL					/* nb_hex		*/
};

static PyTypeObject OracleDateType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"OracleDate",				/* tp_name		*/
	sizeof(OracleDate),			/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) OracleDate_dealloc,	/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) OracleDate_getattr,	/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) OracleDate_cmp,		/* tp_compare		*/
	(reprfunc) OracleDate_repr,		/* tp_repr		*/
	&OracleDateNumberMethods,		/* tp_as_number		*/
	NULL,					/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) OracleDate_str,		/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	OracleDateType__doc__			/* docstring		*/

};

static PyTypeObject OracleRowIDType = {

	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size		*/
	"OracleRowID",				/* tp_name		*/
	sizeof(OracleRowID),			/* tp_basicsize		*/
	0,					/* tp_itemsize		*/
	/* methods */
	(destructor) OracleRowID_dealloc,	/* tp_dalloc		*/
	(printfunc) NULL,			/* tp_print		*/
	(getattrfunc) NULL,			/* obsolete tp_getattr	*/
	(setattrfunc) NULL,			/* obsolete tp_setattr	*/
	(cmpfunc) OracleRowID_cmp,		/* tp_compare		*/
	(reprfunc) OracleRowID_repr,		/* tp_repr		*/
	NULL,					/* tp_as_number		*/
	NULL,					/* tp_as_sequence	*/
	NULL,					/* tp_as_mapping	*/
	(hashfunc) NULL,			/* tp_hash		*/
	(ternaryfunc) NULL,			/* tp_call		*/
	(reprfunc) OracleRowID_str,		/* tp_str		*/
	(getattrofunc) NULL,			/* tp_getattro		*/
	(setattrofunc) NULL,			/* tp_setattro		*/
	0L, 0L,
	OracleRowIDType__doc__			/* docstring		*/

};

/*
** Module level methods
*/

static struct PyMethodDef ServerContext_methods[] = {
	{"cursor", (PyCFunction) ServerContext_cursor, METH_VARARGS,
		"allocate a cursor"
	},
	{"close", (PyCFunction) ServerContext_close, METH_VARARGS,
		"close a connection"
	},
	{"commit", (PyCFunction) ServerContext_commit, METH_VARARGS,
		"commit a transaction"
	},
	{"prepare", (PyCFunction) ServerContext_prepare, METH_VARARGS,
		"prepare to commit a transaction"
	},
	{"rollback", (PyCFunction) ServerContext_rollback, METH_VARARGS,
		"rollback a transaction"
	},
	{"describe", (PyCFunction) ServerContext_describe, METH_VARARGS,
		"describe a database object"
	},
	{"BindingArray", (PyCFunction) ServerContext_BindingArrayObject,
		METH_VARARGS, "allocate a BindingArray(elements, size, type)"},
	{"LobLocator", (PyCFunction) LobLocator_alloc, METH_VARARGS,
		"allocate a LobLocator"},
	{"rowid", (PyCFunction) ServerContext_rowid, METH_VARARGS,
		"allocate an empty rowid"},
	{"getTransaction", (PyCFunction) ServerContext_getTransaction,
		METH_VARARGS,
		"get the current transaction context"},
	{"newTransaction", (PyCFunction) ServerContext_newTransaction,
		METH_VARARGS,
		"get a new transaction context"},
	{"setTransaction", (PyCFunction) ServerContext_setTransaction,
		METH_VARARGS,
		"set the current transaction context"},
	{ NULL, NULL }
};

static struct PyMethodDef TransactionContext_methods[] = {
	{"setName", (PyCFunction) TransactionContext_setName,
		METH_VARARGS,
		"set the name on a transaction"},
	{"setXID", (PyCFunction) TransactionContext_setXID,
		METH_VARARGS,
		"set the XID on a transaction"},
	{"flushCache", (PyCFunction) TransactionContext_flushCache,
		METH_VARARGS,
		"set the XID on a transaction"},
	{ NULL, NULL }
};

static struct PyMethodDef LobLocator_methods[] = {
	{"length", (PyCFunction) LobLocator_length, METH_VARARGS,
		"length of the LOB"},
	{"read", (PyCFunction) LobLocator_read, METH_VARARGS,
		"read([n, offset, csid, csfrm]) -- "
		"read from the LOB\n\n"
		"read n (or all) bytes starting at offset (default 1) using\n"
		"a character set id (default 0) and character set form "
		"(default SQLCS_IMPLICIT)\n"
	},
	{"write", (PyCFunction) LobLocator_write, METH_VARARGS,
		"write(s, [offset, csid, csfrm]) -- "
		"write a string 's' to the LOB\n\n"
		"write starting at offset (default 1) using a character "
		"set id (default 0)\n"
		"and character set form (default SQLCS_IMPLICIT)"
	},
	{"trim", (PyCFunction) LobLocator_trim, METH_VARARGS,
		"trim(size) -- "
		"trim the LOB to the given size\n"
		"The size must be less than or equal to the current length"
	},
	{"open", (PyCFunction) LobLocator_open, METH_VARARGS,
		"open([write]) -- "
		"Open the LOB for reading (File LOBs)\n"
		"The write parameter, if true, specifies that the LOB is\n"
		"to be opened for writing (illegal for FILE lobs)"
	},
	{"close", (PyCFunction) LobLocator_close, METH_VARARGS,
		"close() -- "
		"Close the LOB (File LOBs)"
	},
	{"getFileName", (PyCFunction) LobLocator_getFileName, METH_VARARGS,
		"getFileName() -- "
		"Gets the directory alias and file name (File LOBs)"
	},
	{"setFileName", (PyCFunction) LobLocator_setFileName, METH_VARARGS,
		"setFileName(diralias, filename) -- "
		"Sets the directory alias and file name (File LOBs)"
	},
	{"fileExists", (PyCFunction) LobLocator_fileExists, METH_VARARGS,
		"fileExists() -- "
		"Tests to see if the external file exists (File LOBs)"
	},
	{ NULL, NULL }
};

static struct PyMethodDef Cursor_methods[] = {
	{"close", (PyCFunction) Cursor_close, METH_VARARGS,
		"close() -- close a cursor"},
	{"prepare", (PyCFunction) Cursor_prepare, METH_VARARGS,
		"prepare(statement) -- prepare (parse) a statement"},
	{"bindbypos", (PyCFunction) Cursor_bindbypos, METH_VARARGS,
		"bindbypos(position, value[, type]) -- bind a statement by "
		"position, with a possible type override"
		},
	{"bindbyname", (PyCFunction) Cursor_bindbyname, METH_VARARGS,
		"bindbyname(name, value[, type]) -- bind a statement by name"
		", with a possible type override"},
	{"execute", (PyCFunction) Cursor_execute, METH_VARARGS,
		"execute() -- execute a statement"},
	{"describe", (PyCFunction) Cursor_describe, METH_VARARGS,
		"describe() -- return a list of tuples describing "
		"statement results"},
	{"fetch", (PyCFunction) Cursor_fetch, METH_VARARGS,
		"fetch(n) -- fetch N query results"},
	{"setPrefetch", (PyCFunction) Cursor_setPrefetch, METH_VARARGS,
		"setPrefetch(records[, size]) -- set prefetch buffer size to"
		" records, with an optional size constraint"},
	{"rowcount", (PyCFunction) Cursor_rowcount, METH_VARARGS,
		"rowcount() -- return the number of rows processed"},
	{ NULL, NULL }
};

static struct PyMethodDef ResultSet_methods[] = {
	{ NULL, NULL }
};

static struct PyMethodDef ResultSetItem_methods[] = {
	{"value", (PyCFunction) ResultSetItem_value, METH_VARARGS,
		"value() -- return the value of an item" },
	{ NULL, NULL }
};

static struct PyMethodDef OracleDate_methods[] = {
	{ NULL, NULL }
};

static struct PyMethodDef BindingArrayObject_methods[] = {
	{"setStatic", (PyCFunction) BindingArrayObject_setStatic, METH_VARARGS,
		"Change the BindingArray to static binding"},
	{"type", (PyCFunction) BindingArrayObject_type, METH_VARARGS,
		"Get/Set the datatype of the BindingArray"},
	{"width", (PyCFunction) BindingArrayObject_width, METH_VARARGS,
		"Get the width of the BindingArray"},
	{ NULL, NULL }
};

static struct PyMethodDef dco2_methods[] = {
	{"connect", (PyCFunction) Connect, METH_VARARGS,
		"connect(userid, password, database) -- connect to the "
		"database; userid, password, and database may not be None"},
	{"TypeTable", (PyCFunction) TypeTable_lookup, METH_VARARGS,
		"TypeTable(type) -- Convert a type by type table"},
	{"Traceback", (PyCFunction) Traceback, METH_VARARGS,
		"Traceback() -- return trace table"},
	{"OracleDate", (PyCFunction) OracleDate_alloc, METH_VARARGS,
		"OracleDate(ticks) -- allocate a new OracleDate from ticks "},
	{ NULL, NULL }
};


/* Oracle type table */

#define TYPEENTRY(x) { x, #x, -1, 0, NULL }
#define TYPOENTRY(x) { x, #x, -1, 0, convertOut_##x }
#define CTYPEENTRY(x,y,z) { x, #x, y, z, NULL }
#define CTYPOENTRY(x,y,z) { x, #x, y, z, convertOut_##x }

struct TypeTableStruct {
	int	type;
	char	*name;
	int	ctype;
	int	cwidth;
	PyObject *(*convertout)(int type, int size, int scale, int precision,
		void *start, ServerContext *sc);
};

/* Oracle external type table.  Oracle internal types are marked with I */
/* All unhandled internal types should be marked FIXME 			*/

struct TypeTableStruct TypeTable[] = {
/* I */	TYPOENTRY(SQLT_CHR),			/* Char			*/
#if NATIVENUMERIC
/* I */	CTYPOENTRY(SQLT_NUM,SQLT_VNU,sizeof(OCINumber)),/* Number	*/
#else
/* I */	CTYPOENTRY(SQLT_NUM,SQLT_STR,48),	/* Number		*/
#endif
	TYPOENTRY(SQLT_INT),			/* Integer		*/
	TYPOENTRY(SQLT_FLT),			/* Float		*/
	TYPOENTRY(SQLT_STR),			/* String		*/
	TYPOENTRY(SQLT_VNU),			/* Len prefixed number	*/
	TYPEENTRY(SQLT_PDN),			/* Packed decimal number*/
/* I */	CTYPOENTRY(SQLT_LNG, SQLT_LNG, sizeof(LongFetch)),/* Long	*/
	TYPEENTRY(SQLT_VCS),			/* Variable character	*/
	TYPEENTRY(SQLT_NON),			/* NULL/empty PCC desc	*/
/* I */	TYPEENTRY(SQLT_RID),	/* FIXME */	/* Row ID		*/
/* I */	TYPOENTRY(SQLT_DAT),			/* Date			*/
	TYPEENTRY(SQLT_VBI),			/* Binary VCS format	*/
/* I */	TYPOENTRY(SQLT_BIN),			/* Binary data		*/
/* I */	CTYPOENTRY(SQLT_LBI, SQLT_LBI, sizeof(LongFetch)),/* long binary*/
	TYPEENTRY(SQLT_UIN),			/* Unsigned integer	*/
	TYPEENTRY(SQLT_SLS),			/* Display sign leading	*/
	TYPEENTRY(SQLT_LVC),			/* Longer longs (char)	*/
	TYPEENTRY(SQLT_LVB),			/* Longer long (binary) */
/* I */	TYPOENTRY(SQLT_AFC),			/* ANSI Fixed Char	*/
	TYPEENTRY(SQLT_AVC),			/* ANSI Var Char	*/
	CTYPOENTRY(SQLT_CUR, SQLT_RSET, sizeof(void*)),/* Cursor	*/
	CTYPOENTRY(SQLT_RDD, SQLT_RDD, sizeof(void*)),/* Rowid Descr	*/
	TYPEENTRY(SQLT_LAB),			/* Label		*/
	TYPEENTRY(SQLT_OSL),			/* oslabel		*/
/* I */	TYPEENTRY(SQLT_NTY),	/* FIXME */	/* Named Type		*/
/* I */	TYPEENTRY(SQLT_REF),	/* FIXME */	/* Reference type	*/
/* I */	CTYPOENTRY(SQLT_CLOB,SQLT_CLOB,sizeof(void *)),	/* character LOB*/
/* I */	CTYPOENTRY(SQLT_BLOB,SQLT_BLOB,sizeof(void *)),	/* binary LOB	*/
	CTYPOENTRY(SQLT_BFILE,SQLT_BFILE,sizeof(void *)),/* Binary file */
	CTYPOENTRY(SQLT_CFILE,SQLT_CFILE,sizeof(void *)),/* Char file   */
	CTYPOENTRY(SQLT_RSET, SQLT_RSET, sizeof(void*)),/* Result set	*/
	TYPEENTRY(SQLT_NCO),			/* Named collection	*/
	TYPEENTRY(SQLT_VST),			/* OCIString type	*/
	TYPEENTRY(SQLT_ODT),			/* OCIDate type		*/
#ifdef ORACLE8i
	TYPEENTRY(SQLT_DATE),			/* ANSI date		*/
	TYPEENTRY(SQLT_TIME),			/* Time			*/
	TYPEENTRY(SQLT_TIME_TZ),		/* Time with zone	*/
	CTYPOENTRY(SQLT_TIMESTAMP,SQLT_STR,50), /* TYPEENTRY(SQLT_TIMESTAMP) modified by Maan Hamze 3/1/2007 */
	TYPEENTRY(SQLT_TIMESTAMP_TZ),		/* Time stamp with zone	*/
#ifdef ORACLE9i
	TYPEENTRY(SQLT_TIMESTAMP_LTZ),		/* Time stamp localzone	*/
#endif
	TYPEENTRY(SQLT_INTERVAL_YM),		/* Interval year - month*/
	TYPEENTRY(SQLT_INTERVAL_DS),		/* Interval day - second*/
#endif
#define PLSQL_RECORD 250
#define PLSQL_TABLE 251
#define PLSQL_BOOLEAN 252
	TYPEENTRY(PLSQL_RECORD),		/* PL/SQL record	*/
	TYPEENTRY(PLSQL_TABLE),			/* PL/SQL table		*/
	TYPEENTRY(PLSQL_BOOLEAN),		/* PL/SQL boolean	*/
	{0, NULL, 0, 0, NULL}
};

/*
** Describe tables
*/


#define BYTE (0x01)
#define HALF (0x02)
#define WORD (0x04)
#define TEXTS (0x08)
#define TABLE (0x10)
#define FUNC (0x20)
#define PTR  (0x40)
#define SIGNED (0x80)

#define DTE(a,t,s,T) { #a, a, t, s, (void *) T }

#ifdef ORACLE8i
static DescriptorEntry SchemaDescriptors[] = {
	DTE(OCI_ATTR_LIST_OBJECTS, PTR|TEXTS, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};
#endif

static DescriptorEntry DatabaseDescriptors[] = {
	DTE(OCI_ATTR_VERSION, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_CHARSET_ID, HALF, 0, NULL),
#ifdef ORACLE8i
	DTE(OCI_ATTR_NCHARSET_ID, HALF, 0, NULL),
	DTE(OCI_ATTR_LIST_SCHEMAS, PTR|WORD|TABLE, 0, SchemaDescriptors),
	DTE(OCI_ATTR_MAX_PROC_LEN, WORD, 0, NULL),
	DTE(OCI_ATTR_MAX_COLUMN_LEN, WORD, 0, NULL),
	DTE(OCI_ATTR_CURSOR_COMMIT_BEHAVIOR, BYTE, 0, NULL),
	DTE(OCI_ATTR_MAX_CATALOG_NAMELEN, BYTE, 0, NULL),
	DTE(OCI_ATTR_CATALOG_LOCATION, BYTE, 0, NULL),
	DTE(OCI_ATTR_SAVEPOINT_SUPPORT, BYTE, 0, NULL),
	DTE(OCI_ATTR_NOWAIT_SUPPORT, BYTE, 0, NULL),
	DTE(OCI_ATTR_AUTOCOMMIT_DDL, BYTE, 0, NULL),
	DTE(OCI_ATTR_LOCKING_MODE, BYTE, 0, NULL),
#endif
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry ArgDescriptors[] = {
	DTE(OCI_ATTR_NAME, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_POSITION, HALF, 0, NULL),
	DTE(OCI_ATTR_DATA_TYPE, HALF, 0, NULL),
	DTE(OCI_ATTR_DATA_SIZE, HALF, 0, NULL),
	DTE(OCI_ATTR_PRECISION, BYTE, 0, NULL),
	DTE(OCI_ATTR_SCALE, BYTE|SIGNED, 0, NULL),
	DTE(OCI_ATTR_HAS_DEFAULT, BYTE, 0, NULL),
	DTE(OCI_ATTR_LIST_ARGUMENTS, PTR|WORD|TABLE, 0, ArgDescriptors),
	DTE(OCI_ATTR_IOMODE, WORD, 0, NULL), /* sizeof(OCITypeParamMode) */
	DTE(OCI_ATTR_RADIX, BYTE, 0, NULL),
	DTE(OCI_ATTR_IS_NULL, BYTE, 0, NULL),
	DTE(OCI_ATTR_CHARSET_ID, HALF, 0, NULL),
	DTE(OCI_ATTR_CHARSET_FORM, HALF, 0, NULL),
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry ColDescriptors[] = {
	DTE(OCI_ATTR_DATA_SIZE, HALF, 0, NULL),
	DTE(OCI_ATTR_DATA_TYPE, HALF, 0, NULL),
	DTE(OCI_ATTR_NAME, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_PRECISION, BYTE, 0, NULL),
	DTE(OCI_ATTR_SCALE, BYTE|SIGNED, 0, NULL),
	DTE(OCI_ATTR_IS_NULL, BYTE, 0, NULL),
	DTE(OCI_ATTR_SCHEMA_NAME, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_CHARSET_ID, HALF, 0, NULL),
	DTE(OCI_ATTR_CHARSET_FORM, BYTE, 0, NULL),
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry SeqDescriptors[] = {
	DTE(OCI_ATTR_OBJID, WORD, 0, NULL),
	DTE(OCI_ATTR_MIN, PTR|BYTE|FUNC, 0, numberConversion),
	DTE(OCI_ATTR_MAX, PTR|BYTE|FUNC, 0, numberConversion),
	DTE(OCI_ATTR_INCR, PTR|BYTE|FUNC, 0, numberConversion),
	DTE(OCI_ATTR_CACHE, PTR|BYTE|FUNC, 0, numberConversion),
	DTE(OCI_ATTR_ORDER, BYTE, 0, NULL),
	DTE(OCI_ATTR_HW_MARK, PTR|BYTE|FUNC, 0, numberConversion),
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry SynDescriptors[] = {
	DTE(OCI_ATTR_OBJID, WORD, 0, NULL),
	DTE(OCI_ATTR_SCHEMA_NAME, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_NAME, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_LINK, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry ProcDescriptors[] = {
	DTE(OCI_ATTR_LIST_ARGUMENTS, PTR|WORD|TABLE, 0, ArgDescriptors),
#ifdef ORACLE8i
	DTE(OCI_ATTR_IS_INVOKER_RIGHTS, BYTE, 0, NULL),
#endif
	DTE(OCI_ATTR_NAME, TEXTS|PTR, 64, NULL),
	DTE(OCI_ATTR_OVERLOAD_ID, WORD, 0, NULL),
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry PkgDescriptors[] = {
	DTE(OCI_ATTR_LIST_SUBPROGRAMS, PTR|WORD|TABLE, 0, ProcDescriptors),
#ifdef ORACLE8i
	DTE(OCI_ATTR_IS_INVOKER_RIGHTS, BYTE, 0, NULL),
#endif
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry TableDescriptors[] = {
	DTE(OCI_ATTR_OBJID, WORD, 0, NULL),
	DTE(OCI_ATTR_NUM_COLS, HALF, 0, NULL),
	DTE(OCI_ATTR_LIST_COLUMNS, PTR|WORD|TABLE, 0, ColDescriptors),
#ifdef ORACLE8i
	DTE(OCI_ATTR_IS_TEMPORARY, BYTE, 0, NULL),
#endif
	DTE(OCI_ATTR_TABLESPACE, WORD, 0, NULL),
	DTE(OCI_ATTR_CLUSTERED, BYTE, 0, NULL),
	DTE(OCI_ATTR_PARTITIONED, BYTE, 0, NULL),
	DTE(OCI_ATTR_INDEX_ONLY, BYTE, 0, NULL),
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry DescriptorTable[] = {
	DTE(OCI_PTYPE_TABLE, TABLE, 0, TableDescriptors),
	DTE(OCI_PTYPE_VIEW, TABLE, 0, TableDescriptors),
	DTE(OCI_PTYPE_PROC, TABLE, 0, ProcDescriptors),
	DTE(OCI_PTYPE_FUNC, TABLE, 0, ProcDescriptors),
	DTE(OCI_PTYPE_PKG, TABLE, 0, PkgDescriptors),
	DTE(OCI_PTYPE_SYN, TABLE, 0, SynDescriptors),
	DTE(OCI_PTYPE_SEQ, TABLE, 0, SeqDescriptors),
	DTE(OCI_PTYPE_ARG, TABLE, 0, ArgDescriptors),
	DTE(OCI_PTYPE_COL, TABLE, 0, ColDescriptors),
	DTE(OCI_PTYPE_LIST, FUNC, 0, listDescriptors),
#ifdef ORACLE8i
	DTE(OCI_PTYPE_SCHEMA, TABLE, 0, SchemaDescriptors),
	DTE(OCI_PTYPE_DATABASE, TABLE, 0, DatabaseDescriptors),
#endif
	{NULL, 0, 0, 0, NULL},
};

static DescriptorEntry GlobalDescriptors[] = {
	DTE(OCI_ATTR_NUM_ATTRS, HALF, 0, NULL),
	DTE(OCI_ATTR_NUM_PARAMS, HALF, 0, NULL),
#ifdef ORACLE8i
	DTE(OCI_ATTR_OBJ_ID, WORD, 0, NULL),
	DTE(OCI_ATTR_OBJ_NAME, TEXTS|PTR, 0, NULL),
	DTE(OCI_ATTR_OBJ_SCHEMA, TEXTS|PTR, 0, NULL),
#endif
	DTE(OCI_ATTR_PTYPE, BYTE, 0, NULL),
	{NULL, 0, 0, 0, NULL},
};


/* -----------------------------------------------------------------------
**
** Functions
**
** -----------------------------------------------------------------------
*/

/*
** RaiseOCIError
**
** Spit out an OCI error message
*/

#define RaiseOCIError(hp, type) RaiseOCIError1(hp, type, 1)
#define NoticeOCIError(hp, type) RaiseOCIError1(hp, type, 0)
static PyObject *RaiseOCIError1(dvoid *hp, ub4 handle_type, int setpy) {
	text buff[OCI_ERROR_MAXMSG_SIZE];
	sb4	errcode;
	PyObject *eo;

	eo = ErrorObject;

	OCIErrorGet(hp, 1, NULL, &errcode, buff, sizeof(buff), handle_type);

	if (errcode == 1013) eo = PyExc_KeyboardInterrupt;

	if (setpy == 1)
		PyErr_SetObject(eo, Py_BuildValue("is#", (int) errcode, buff,
			strlen(buff)-1));
#ifndef DCO2ERRPRINT
	else
#endif
		fprintf(stderr,"%s\n", buff);

	TRACE(T_OERROR, ("sRS","RaiseOCIError",errcode,buff));

	if (traceDump != NULL) Tracedump1();

	return NULL;
}

/*
** Raise an invalid handle error
**
*/

static PyObject *RaiseInvalidHandle(void) {

	PyErr_SetObject(ProgrammingErrorObject, Py_BuildValue("s",
		"Invalid OCI Handle Used"));

	TRACE(T_OERROR, ("s","RaiseInvalidHandle"));

	if (traceDump != NULL) Tracedump1();

	return NULL;
}


/*
** Connect
**
** Connect to Oracle; allocate a ServerContext and return it
**
** N.B. Not every call is wrapped in Py_BEGIN_ALLOW_THREADS since most of
** these calls do not involve a server round trip
*/

static PyObject *Connect(PyObject *self, PyObject *args) {
	ServerContext *sc;
	char	*user;
	char	*password;
	char	*database;
	int	userlen;
	int	passwordlen;
	int	databaselen;
	sword	status;
	sword	credentials = OCI_CRED_RDBMS;

	if (!PyArg_ParseTuple(args, "s#s#s#",
		&user, &userlen,
		&password, &passwordlen,
		&database, &databaselen)) return NULL;

	TRACE(T_ENTRY,("sSSS","Connect",user,password,database));

	if ((sc = PyObject_NEW(ServerContext, &ServerContextType)) == NULL)
		return NULL;

	sc->envhp = NULL;
	sc->errhp = NULL;
	sc->svchp = NULL;
	sc->srvhp = NULL;
	sc->usrhp = NULL;
	sc->tctx = NULL;

#ifdef COMMONENVIRONMENT
	if (GLBenvs == 0) {
#endif

#ifdef ORACLE8i
	TRACE(T_HCALL,("sA", "OCIEnvCreate", &sc->envhp));
	status = OCIEnvCreate(&sc->envhp, OCI_THREADED
#ifdef USEOBJECT
		|OCI_OBJECT
#endif
		, NULL, NULL, NULL, NULL, 0, NULL);
	TRACE(T_HRETURN,("sRA", "OCIEnvCreate", status, sc->envhp));
	if (status != OCI_SUCCESS) {
		fprintf(stderr, "OCIEnvCreate failed: %s:%d",
			__FILE__,__LINE__ - 3);
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}
#else
	TRACE(T_HCALL,("s", "OCIInitialize"));
	status = OCIInitialize(OCI_THREADED
#ifdef USEOBJECT
		|OCI_OBJECT
#endif
		, NULL, NULL, NULL, NULL);
	TRACE(T_HRETURN,("sR", "OCIInitialize", status));

	if (status != OCI_SUCCESS) {
		fprintf(stderr, "OCIInitialize failed: %s:%d",
			__FILE__,__LINE__ - 3);
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}
	TRACE(T_HCALL,("sA","OCIEnvInit",&sc->envhp));
	status =OCIEnvInit(&sc->envhp, OCI_DEFAULT, 0, NULL);
	TRACE(T_HRETURN,("sRA","OCIEnvInit",status, sc->envhp));

	if (status != OCI_SUCCESS) {
		fprintf(stderr, "OCIEnvInit failed: %s:%d",
			__FILE__,__LINE__ - 3);
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}
#endif
#ifdef COMMONENVIRONMENT
	GLBenvhp = sc->envhp;
	} else {
		sc->envhp = GLBenvhp;
	}

	GLBenvs++;
#endif

	/* Allocate the error handle */
	TRACE(T_HCALL,("sAs","OCIHandleAlloc",&sc->errhp, "OCI_HTYPE_ERROR"));
	status = OCIHandleAlloc(sc->envhp, (dvoid **)&sc->errhp,
		OCI_HTYPE_ERROR, 0, NULL);
	TRACE(T_RETURN,("sRA","OCIHandleAlloc",status,sc->errhp));

	if (status != OCI_SUCCESS) {
		sc->svchp = NULL;
		sc->errhp = NULL;
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}


	/* Allocate the server handle */

	TRACE(T_HCALL,("sAs","OCIHandleAlloc",&sc->srvhp, "OCI_HTYPE_SERVER"));
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->srvhp,
		OCI_HTYPE_SERVER, 0, NULL);
	TRACE(T_HRETURN,("sRA","OCIHandleAlloc",status,sc->srvhp));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}

	/* Connect to database */

	if (databaselen == 0)
		database = NULL;			/* Safety */

	TRACE(T_CALL,("s","OCIServerAttach"));

	Py_BEGIN_ALLOW_THREADS
	status = OCIServerAttach(sc->srvhp, sc->errhp, database, databaselen,
		OCI_DEFAULT);
	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR","OCIServerAttach", status));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->errhp, OCI_HTYPE_ERROR);
		Py_DECREF(sc);
		return NULL;
	}

	/* Allocate a service context */

	TRACE(T_HCALL,("sAs","OCIHandleAlloc",&sc->srvhp, "OCI_HTYPE_SVCCTX"));
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->svchp,
		OCI_HTYPE_SVCCTX, 0, NULL);
	TRACE(T_HRETURN,("sRA","OCIHandleAlloc",status,sc->svchp));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}

	/* Attach the server to the service context */

	TRACE(T_HCALL,("ssAsA", "OCIAttrSet", "OCI_HTYPE_SVCCTX", sc->svchp,
		"OCI_ATTR_SERVER", sc->srvhp));
	status = OCIAttrSet(sc->svchp, OCI_HTYPE_SVCCTX, (dvoid *) sc->srvhp,
		(ub4) 0, OCI_ATTR_SERVER, sc->errhp);
	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}

	/* Allocate a session */
	TRACE(T_HCALL,("sAs","OCIHandleAlloc",&sc->usrhp, "OCI_HTYPE_SESSION"));
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->usrhp,
		OCI_HTYPE_SESSION, 0, NULL);
	TRACE(T_HRETURN,("sRA","OCIHandleAlloc",status,sc->usrhp));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(sc);
		return NULL;
	}


	/* Were credentials provided? if so, set them in the session */

	if (user[0] || password[0]) {
		TRACE(T_HCALL,("ssAsSd", "OCIAttrSet", "OCI_HTYPE_SESSION",
			sc->usrhp, "OCI_ATTR_USERNAME", user, userlen));
		status = OCIAttrSet(sc->usrhp, OCI_HTYPE_SESSION,
			(dvoid *) user, (ub4) userlen,
			OCI_ATTR_USERNAME, sc->errhp);
		TRACE(T_HRETURN,("sR", "OCIAttrSet", status));
		if (status != OCI_SUCCESS) {
			RaiseOCIError(sc->errhp, OCI_HTYPE_ERROR);
			Py_DECREF(sc);
			return NULL;
		}
		TRACE(T_HCALL,("ssAsSd", "OCIAttrSet", "OCI_HTYPE_SESSION",
			sc->svchp, "OCI_ATTR_PASSWORD", password, passwordlen));
		status = OCIAttrSet(sc->usrhp, OCI_HTYPE_SESSION,
			(dvoid *) password, (ub4) passwordlen,
			OCI_ATTR_PASSWORD, sc->errhp);
		TRACE(T_HRETURN,("sR", "OCIAttrSet", status));
		if (status != OCI_SUCCESS) {
			RaiseOCIError(sc->errhp, OCI_HTYPE_ERROR);
			Py_DECREF(sc);
			return NULL;
		}
	} else
		credentials = OCI_CRED_EXT;	/* External credentials */

	/* Now begin the session */


	TRACE(T_CALL,("sd","OCISessionBegin", credentials));

	Py_BEGIN_ALLOW_THREADS
	status = OCISessionBegin(sc->svchp, sc->errhp, sc->usrhp,
		credentials, OCI_DEFAULT);
	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR","OCISessionBegin",status));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->errhp, OCI_HTYPE_ERROR);
		Py_DECREF(sc);
		return NULL;
	}

	/* Now attach the session to the service context */

	TRACE(T_HCALL,("ssAsA", "OCIAttrSet", "OCI_HTYPE_SVCCTX",
		sc->svchp, "OCI_ATTR_SESSION", sc->usrhp));
	status = OCIAttrSet(sc->svchp, OCI_HTYPE_SVCCTX,
		sc->usrhp, 0, OCI_ATTR_SESSION, sc->errhp);
	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->errhp, OCI_HTYPE_ERROR);
		Py_DECREF(sc);
		return NULL;
	}

	TRACE(T_EXIT,("sA","Connect",sc));

	return OBJECT(sc);
}

/*
** ServerContext_close
**
** Called to hang up the connection to the database
*/

static PyObject *ServerContext_close(ServerContext *self,PyObject *args) {

	sword status;
	ub4 errcode;
	char errbuf[OCI_ERROR_MAXMSG_SIZE];

	TRACE(T_ENTRY, ("sA", "ServerContext_close", self));

	Py_XDECREF(self->tctx);
	self->tctx = NULL;

	/* End the session */

	if (self->svchp) {
		TRACE(T_CALL,("s","OCISessionEnd"));
		Py_BEGIN_ALLOW_THREADS
		status = OCISessionEnd(self->svchp, self->errhp, self->usrhp,
			OCI_DEFAULT);
		Py_END_ALLOW_THREADS
		TRACE(T_RETURN,("sR","OCISessionEnd",status));

		if (status != OCI_SUCCESS) {
			OCIErrorGet(self->errhp, 1, NULL, &errcode,
			errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
			fprintf(stderr, "OCI_ERROR at %s:%d: %s", __FILE__,
				__LINE__ - 8, errbuf);
		}

	}

	/* Disconnect from the server */

	if (self->srvhp) {
		TRACE(T_CALL,("s","OCIServerDetatch"));
		Py_BEGIN_ALLOW_THREADS
		status = OCIServerDetach(self->srvhp, self->errhp,
			OCI_DEFAULT);
		Py_END_ALLOW_THREADS
		TRACE(T_RETURN,("sR","OCIServerDetatch",status));

		if (status != OCI_SUCCESS) {
			OCIErrorGet(self->errhp, 1, NULL, &errcode,
			errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
			fprintf(stderr, "OCI_ERROR at %s:%d: %s", __FILE__,
				__LINE__ - 8, errbuf);
		}

	}


	TRACE(T_EXIT, ("s", "ServerContext_close"));

	Py_INCREF(Py_None);

	return Py_None;
}

/*
** ServerContext_dealloc
**
** Called when a server context goes out of scope; hang up the connection
** to the database!
*/

static void ServerContext_dealloc(ServerContext *self) {

	PyObject *obj;
	sword status;

	TRACE(T_ENTRY, ("sA", "ServerContext_dealloc", self));

	obj = ServerContext_close(self, Py_None);
	Py_XDECREF(obj);

	/* Deallocate the session handle */

        if (self->usrhp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_SESSION",
			self->usrhp));
		status = OCIHandleFree(self->usrhp, OCI_HTYPE_SESSION);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
		if (status != OCI_SUCCESS) {

                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
		self->usrhp = NULL;
	}

	/* Deallocate the server handle */

        if (self->srvhp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_SERVER",
			self->srvhp));
		status = OCIHandleFree(self->srvhp, OCI_HTYPE_SERVER);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
		if (status != OCI_SUCCESS) {

                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
		self->srvhp = NULL;
	}


	/* Deallocate the service context handle */

	if (self->svchp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_SVCCTX",
			self->svchp));
		status = OCIHandleFree(self->svchp, OCI_HTYPE_SVCCTX);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
                if (status != OCI_SUCCESS) {

                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
		self->svchp = NULL;
        }

	/* Deallocate the error handle */

        if (self->errhp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_ERROR",
			self->errhp));
		status = OCIHandleFree(self->errhp, OCI_HTYPE_ERROR);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
		if (status != OCI_SUCCESS) {

                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
		self->errhp = NULL;
	}

	/* Deallocate the environment handle */

#ifdef COMMONENVIRONMENT
	if (GLBenvs == 1) {
#endif
		if (self->envhp) {
			TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_ENV",
				self->envhp));
			status = OCIHandleFree(self->envhp, OCI_HTYPE_ENV);
			TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
			if (status != OCI_SUCCESS) {
				fprintf(stderr,"OCI_ERROR %d at %s:%d\n",
					status, __FILE__,__LINE__-4);
			}
		}
		self->envhp = NULL;

#ifdef COMMONENVIRONMENT
	} else {
		self->envhp = NULL;
	}

	GLBenvs--;
#endif


	TRACE(T_EXIT, ("s", "ServerContext_dealloc"));

	PyObject_Del(self);
}

/*
** ServerContext_getattr
*/

static PyObject *ServerContext_getattr(ServerContext *self, char *name) {
	PyObject *obj;

	TRACE(T_ENTRY,("sAS","ServerContext_getattr", self, name));

	obj = Py_FindMethod(ServerContext_methods, OBJECT(self), name);

	TRACE(T_EXIT,("sA","ServerContext_getattr", obj));

	return obj;
}

/*
** ServerContext_commit
*/

static PyObject *ServerContext_commit(ServerContext *self, PyObject *args) {
	int status;
	int twophase = 0;
	ub4 flags = OCI_DEFAULT;

	TRACE(T_ENTRY,("sAA","ServerContext_commit",self,args));

	if (!PyArg_ParseTuple(args, "|i:commit", &twophase))
		return NULL;

	if (twophase == 2)
		flags = OCI_TRANS_TWOPHASE;

	TRACE(T_CALL,("sd","OCITransCommit", flags));

	Py_BEGIN_ALLOW_THREADS
	status = OCITransCommit(self->svchp, self->errhp, flags);
	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR","OCITransCommit", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	Py_XDECREF(self->tctx);
	self->tctx = NULL;

	Py_INCREF(Py_None);

	TRACE(T_EXIT,("s","ServerContext_commit"));

	return Py_None;
}

/*
** ServerContext_prepare
*/

static PyObject *ServerContext_prepare(ServerContext *self, PyObject *args) {
	int status;
	ub4 flags = OCI_DEFAULT;

	TRACE(T_ENTRY,("sAA","ServerContext_prepare",self,args));

	TRACE(T_CALL,("sd","OCITransPrepare",flags));

	Py_BEGIN_ALLOW_THREADS
	status = OCITransPrepare(self->svchp, self->errhp, flags);
	Py_END_ALLOW_THREADS

	TRACE(T_ENTRY,("sR","OCITransPrepare",status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	Py_INCREF(Py_None);

	TRACE(T_EXIT,("sAA","ServerContext_prepare"));

	return Py_None;
}

/*
** ServerContext_rollback
*/

static PyObject *ServerContext_rollback(ServerContext *self, PyObject *args) {
	int status;
	ub4 flags = OCI_DEFAULT;

	TRACE(T_ENTRY,("sAA","ServerContext_rollback",self,args));

	TRACE(T_CALL,("sd","OCITransRollback",flags));

	Py_BEGIN_ALLOW_THREADS
	status = OCITransRollback(self->svchp, self->errhp, flags);
	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sd","OCITransRollback",status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);


	Py_XDECREF(self->tctx);
	self->tctx = NULL;

	Py_INCREF(Py_None);

	TRACE(T_ENTRY,("s","ServerContext_rollback"));

	return Py_None;
}

/*
** ServerContext_cursor
**
** Create a new cursor
*/

static PyObject *ServerContext_cursor(ServerContext *self, PyObject *args) {
	Cursor *cursor;
	int i;
	sword status;

	TRACE(T_ENTRY,("sAA","ServerContext_cursor",self,args));

	/* Ignore args
	if (!PyArg_ParseTuple(args,"")) return NULL;	 / * Need no args */

	if ((cursor = PyObject_NEW(Cursor, &CursorType)) == NULL) return NULL;

	cursor->stmtp = NULL;
	cursor->errhp = NULL;

	TRACE(T_HCALL,("sAs", "OCIHandleAlloc", &cursor->stmtp,
		"OCI_HTYPE_STMT"));
	status = OCIHandleAlloc(self->envhp, (dvoid **) &cursor->stmtp,
		OCI_HTYPE_STMT, 0, 0);
	TRACE(T_HRETURN,("sRA", "OCIHandleAlloc", status, cursor->stmtp));

	if (status != OCI_SUCCESS) {

		RaiseOCIError(self->envhp, OCI_HTYPE_ENV);
		Py_DECREF(cursor);
		return NULL;
	}

	/* Allocate the per-cursor error handle */
	TRACE(T_HCALL,("sAs","OCIHandleAlloc",&cursor->errhp,
		"OCI_HTYPE_ERROR"));
	status = OCIHandleAlloc(self->envhp, (dvoid **)&cursor->errhp,
		OCI_HTYPE_ERROR, 0, NULL);
	TRACE(T_RETURN,("sRA","OCIHandleAlloc",status,cursor->errhp));

	if (status != OCI_SUCCESS) {
		cursor->errhp = NULL;
		RaiseOCIError(self->envhp, OCI_HTYPE_ENV);
		Py_DECREF(cursor);
		return NULL;
	}

	cursor->sc = self;
	cursor->definition = NULL;
	cursor->results = NULL;
	cursor->current = -1;
	cursor->count = 0;
	cursor->batchct = -1;
	cursor->batchsz = 0;
	cursor->flags = 0;
	cursor->type = 0;
	cursor->longcol = 0;

	for (i = 0; i < MAX_BIND_COUNT; i++) {
		cursor->bind[i].bindp = NULL;
		cursor->bind[i].object = NULL;
		cursor->bind[i].flags = 0;
	}

	Py_INCREF(self);		/* Assocation with context */

	TRACE(T_EXIT,("sA","ServerContext_cursor",cursor));

	return OBJECT(cursor);
}

/*
** listColumns
**
** Part of the schema describe functions; generates a list of values for
** each entry in a list.
*/

static PyObject *listColumns(ServerContext *self, OCIParam *parmh,
	int attribute, DescriptorEntry *entry, PyObject *current) {

	PyObject *result;
	PyObject *temp;
	ub2	type = 0;
	ub2	columns = 0;
	sword	status;
	OCIParam *cparamh;
	ub4 i = 1;


	TRACE(T_ENTRY,("sAAdAA", "listColumns", self, parmh, attribute, entry,
		current));

	if (parmh == NULL) {
		Py_INCREF(Py_None);
		TRACE(T_EXIT,("ss", "listColumns", "None"));
		return Py_None;
	}

#if 0
#ifdef ORACLE8i
	TRACE(T_HCALL,("sAsAs", "OCIAttrGet", parmh,
		"OCI_DTYPE_PARAM", &type, "OCI_ATTR_LTYPE"));
	status = OCIAttrGet((dvoid *) parmh, OCI_DTYPE_PARAM,
			&type, 0, OCI_ATTR_LTYPE, self->errhp);
	TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, type));
#else
	/* FIXME This is probably wrong, but I cant test it yet */
	/* We want the LIST TYPE, but OCI_ATTR_LTYPE is not available in
	** Oracle 8.0.
	**
	** Potential workaround:  we only need the type code to figure
	** out the list enumeration strategy (zero or one based)
	** so it would be possible to skip the list type description
	** and probe for column 0 and column N, discarding errors.
	** It would also be possible to encode the expected list type in
	** the parameter description tables, and pass that as an argument
	** from listDescriptors
	*/
	TRACE(T_HCALL,("sAsAs", "OCIAttrGet", parmh,
		"OCI_DTYPE_PARAM", &type, "OCI_ATTR_PTYPE"));
	status = OCIAttrGet((dvoid *) parmh, OCI_DTYPE_PARAM,
			&type, 0, OCI_ATTR_PTYPE, self->errhp);
	TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, type));
#endif

	if (status != OCI_SUCCESS) return RaiseOCIError(self->errhp,
		OCI_HTYPE_ERROR);
#else
	/* Attribute based type workaround */
	/*
	if (attribute == OCI_ATTR_LIST_SUBPROGRAMS)
		type = OCI_LTYPE_SUBPRG;
	else if (attribute == OCI_ATTR_LIST_ARGUMENTS)
		type = OCI_LTYPE_ARG_FUNC;
	else */type = 0;

#endif

	TRACE(T_HCALL,("sAsAs", "OCIAttrGet", parmh,
		"OCI_DTYPE_PARAM", &columns, "OCI_ATTR_NUM_PARAMS"));
	status = OCIAttrGet((dvoid *) parmh, OCI_DTYPE_PARAM,
			&columns, 0, OCI_ATTR_NUM_PARAMS, self->errhp);
	TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, columns));

	if (status != OCI_SUCCESS) return RaiseOCIError(self->errhp,
		OCI_HTYPE_ERROR);

	/* OK, I wish Oracle got their act together on this */

	/*
	if (type == OCI_LTYPE_ARG_FUNC || type == OCI_LTYPE_SUBPRG)
		i = 0;
	else
		columns++;
	*/

	result = PyList_New(0);

	/*
	if (i == 1) {
		Py_INCREF(Py_None);
		PyList_Append(result, Py_None);
	}
	*/

	i = 0;

	/* Fudge the columns to not scan empty packages */
	if (attribute == OCI_ATTR_LIST_SUBPROGRAMS && columns == 0) i = 1;

	for (; i <= columns; i++) {
		TRACE(T_HCALL,("sAsAd", "OCIParamGet", parmh,
			"OCI_DTYPE_PARAM", &cparamh, i));
		status = OCIParamGet((dvoid *) parmh, OCI_DTYPE_PARAM,
				self->errhp, (dvoid *) &cparamh, i);
		TRACE(T_HRETURN,("sRA", "OCIParamGet", status, cparamh));

		if (status == OCI_SUCCESS)  {
			temp = listDescriptors(self, cparamh, entry,
				current);
			if (temp != NULL) {
				PyList_Append(result, temp);
				Py_DECREF(temp);
			}
		} /* else {
			Py_DECREF(result);
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		} */ /* Eat errors here */

		TRACE(T_HCALL,("ssA","OCIDescriptorFree","OCI_DTYPE_PARAM",
			cparamh));
		status = OCIDescriptorFree(cparamh, OCI_DTYPE_PARAM);
		TRACE(T_HRETURN,("sR","OCIDescriptorFree", status));

	}

	TRACE(T_EXIT,("sA", "listColumns", result));

	return result;
}

/*
** listDescriptors
**
** For schema descriptions.
**
** Go through the descriptors in a table, and accumulate values for all
** keys
*/

static PyObject *listDescriptors(ServerContext *self, OCIParam *parmh,
	DescriptorEntry *entry, PyObject *current) {
	sword status;
	sword size;
	sword *sizep;
	ub4 buff[256];
	ub4 *word;
	ub2 *half;
	ub1 *byte;
	sb1 *sbyte;
	dvoid *ptr;
	PyObject *result;
	PyObject *value;
	long l;
	int twopass = 0;


	TRACE(T_ENTRY,("sAAAA", "listDescriptors", self, parmh, entry,
		current));

	ptr = (dvoid *) buff;
	word = (ub4 *) ptr;
	half = (ub2 *) ptr;
	byte = (ub1 *) ptr;
	sbyte = (sb1 *) ptr;

	if (entry == GlobalDescriptors) twopass = 1;

	if (entry == NULL) {
		Py_INCREF(Py_None);
		TRACE(T_EXIT,("ss", "listDescriptors", "None"));

		return Py_None;
	}

	result = (PyObject *) PyDict_New();

	while (entry && entry->attribute != NULL) {

		if (entry->rtype & PTR && !(entry->rtype & (BYTE|HALF|WORD))) {
			size = sizeof(buff);
			sizep = &size;
		} else {
			size = 0;
			sizep = NULL;
		}

		buff[0] = 0;
		buff[1] = 0;

		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", parmh,
			"OCI_DTYPE_PARAM", ptr, entry->attribute));
		status = OCIAttrGet((dvoid *) parmh, OCI_DTYPE_PARAM,
			ptr, (dvoid *) sizep, entry->attr, self->errhp);
		TRACE(T_HRETURN,("sRdA", "OCIAttrGet", status, size, *word));
		/* OK, now what? */

		if (status == OCI_SUCCESS) {
			if (entry->rtype == (PTR|WORD|TABLE)) {
				value = listColumns(self,
					(OCIParam *) *word,
					entry->attr,
					(DescriptorEntry *) entry->proc,
					result);
				if (value == NULL) {
					Py_DECREF(result);
					return NULL;
				}
			} else if (entry->rtype & PTR) {
				if (entry->rtype & TEXTS) {
					if ((char *) *word != NULL) {
						value = PyString_FromStringAndSize(
						(char *) *word, size);
					} else {
						Py_INCREF(Py_None);
						value = Py_None;
					}
				} else if (sizep != NULL)  {
					value = PyString_FromStringAndSize(ptr,
						size);
				} else {
					/* XXX This next bit is wrong */
					value = PyString_FromString(ptr);
				}
			} else if (entry->rtype == BYTE) {
				l = *byte;
				value = PyInt_FromLong(l);

			} else if (entry->rtype == (BYTE|SIGNED)) {
				l = *sbyte;
				value = PyInt_FromLong(l);

			} else if (entry->rtype == HALF) {
				l = *half;
				value = PyInt_FromLong(l);

			} else if (entry->rtype == WORD) {
				l = *word;
				value = PyInt_FromLong(l);
			} else {
				Py_INCREF(Py_None);
				value = Py_None;
			}

			PyDict_SetItemString(result, entry->attribute, value);

			Py_DECREF(value);
		}

		entry++;

		/* If we are the global describe, we'll also run the
		** sepecific ptr type
		*/

		if (entry->attribute == NULL && twopass == 1) {
			PyObject *val;
			DescriptorEntry *e;
			unsigned long v;

			twopass = 0;
			val = PyDict_GetItemString(result, "OCI_ATTR_PTYPE");
			if (val != NULL) {
				v = PyInt_AsLong(val);
				e = DescriptorTable;
				while (e->attribute != NULL) {
					if (e->attr == v) {
						entry = (DescriptorEntry *)
							e->proc;
						break;
					}
					e++;
					if (entry->attribute != NULL) break;
				}
			}
		}
	}

	TRACE(T_EXIT,("sA", "listDescriptors", result));

	return result;
}

/*
** ServerContext_describe
**
** Describe a schema object
*/

static PyObject *ServerContext_describe(ServerContext *self, PyObject *args) {
	char	*name;
	int	namelen;
	PyObject *result = NULL;
	sword	status;
	OCIDescribe *deschp = NULL;
	OCIParam *parmh = NULL;
	long 	one = 1;

	if (!PyArg_ParseTuple(args,"s#",&name,&namelen))
		return NULL;	/* String arg	*/

	TRACE(T_ENTRY,("sS", "ServerContext_describe", name));


	TRACE(T_HCALL,("sAs", "OCIHandleAlloc", &deschp, "OCI_HTYPE_DESCRIBE"));
	status = OCIHandleAlloc(self->envhp, (dvoid **) &deschp,
		OCI_HTYPE_DESCRIBE, 0, NULL);
	TRACE(T_HRETURN,("sAR", "OCIHandleAlloc", deschp, status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->envhp, OCI_HTYPE_ENV);

	/* Public synonym enablemement */
	TRACE(T_HCALL,("ssAs", "OCIAttrSet", "OCI_HTYPE_DESCRIBE",
		deschp, "OCI_ATTR_DESC_PUBLIC"));
	status = OCIAttrSet(deschp, OCI_HTYPE_DESCRIBE,
		(dvoid *) &one, (ub4) 4, OCI_ATTR_DESC_PUBLIC, self->errhp);
	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);


	TRACE(T_CALL,("sSsA", "OCIDescribeAny", name, "OCI_PTYPE_UNK",
		deschp));
	status = OCIDescribeAny(self->svchp, self->errhp, name, namelen,
		OCI_OTYPE_NAME, OCI_DEFAULT, OCI_PTYPE_UNK, deschp);
	TRACE(T_RETURN,("sR", "OCIDescribeAny", status));

	if (status == OCI_SUCCESS) {
		TRACE(T_HCALL, ("sAsAs", "OCIAttrGet", deschp,
			"OCI_HTYPE_DESCRIBE", &parmh, "OCI_ATTR_PARAM"));
		status = OCIAttrGet(deschp, OCI_HTYPE_DESCRIBE, &parmh, 0,
			OCI_ATTR_PARAM, self->errhp);
		TRACE(T_HRETURN, ("sRA", "OCIAttrGet", status, parmh));
	}

	if (status == OCI_SUCCESS) {

		/* Now that we have a describe handle, we can go get all the
		** attributes on it
		*/

		result = listDescriptors(self, parmh, GlobalDescriptors, NULL);


	} else result = RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);


	TRACE(T_HCALL,("sAs", "OCIHandleFree", deschp,
		"OCI_HTYPE_DESCRIBE"));
	status = OCIHandleFree(deschp, OCI_HTYPE_DESCRIBE);
	TRACE(T_RETURN,("sd", "OCIHandleFree", status));

	TRACE(T_EXIT,("sA", "ServerContext_describe", result));

	return result;
}

/*
** ServerContext_rowid
**
** Create an empty rowid object
*/

static PyObject *ServerContext_rowid(ServerContext *self, PyObject *args) {
	PyObject *rowid;

	TRACE(T_ENTRY,("sA", "ServerContext_rowid", self));

	rowid = OracleRowID_alloc(self);

	TRACE(T_EXIT,("sA", "ServerContext_rowid", rowid));
	return rowid;
}

/*
** ServerContext_getTransaction
**
** Return the current transaction object
*/

static PyObject *ServerContext_getTransaction(ServerContext *self,
		PyObject *args) {
	TransactionContext *transaction = NULL;
	sword status;

	TRACE(T_ENTRY,("sA", "ServerContext_getTransaction", self));

	if (self->tctx) {
		transaction = self->tctx;
		Py_INCREF(transaction);
	} else {

		transaction = TransactionContext_alloc(self, 0);

		TRACE(T_HCALL,("ssAsA", "OCIAttrGet", "OCI_HTYPE_SVCCTX",
			self->svchp, "OCI_ATTR_TRANS", &transaction->tranp));
		status = OCIAttrGet(self->svchp, OCI_HTYPE_SVCCTX,
			&transaction->tranp, (ub4) 0, OCI_ATTR_TRANS,
			self->errhp);
		TRACE(T_HRETURN,("sRA", "OCIAttrGet", status,
			transaction->tranp));

		if (status != OCI_SUCCESS) {
			RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
			return NULL;
		}

		if (transaction->tranp == NULL) {
			/* No transaction, return None */
			Py_DECREF(OBJECT(transaction));
			transaction = (TransactionContext *)Py_None;
			Py_INCREF(Py_None);
		}
	}

	TRACE(T_EXIT,("sA", "ServerContext_getTransaction", transaction));
	return OBJECT(transaction);
}

/*
** ServerContext_setTransaction
**
** Set the current transaction object
*/

static PyObject *ServerContext_setTransaction(ServerContext *self,
		PyObject *args) {
	sword status;
	TransactionContext *transaction = NULL;

	TRACE(T_ENTRY,("sA", "ServerContext_setTransaction", self));

	if (!PyArg_ParseTuple(args,"O:setTransaction", &transaction))
		return NULL;

	if (transaction->ob_type != &TransactionContextType) {
		PyErr_SetObject(PyExc_TypeError, OBJECT(transaction));
		return NULL;
	}

	TRACE(T_HCALL,("ssAsA", "OCIAttrSet", "OCI_HTYPE_SVCCTX", self->svchp,
		"OCI_ATTR_TRANS", transaction->tranp));
	status = OCIAttrSet(self->svchp, OCI_HTYPE_SVCCTX,
		(dvoid *) transaction->tranp, (ub4) 0, OCI_ATTR_TRANS,
		self->errhp);
	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		return NULL;
	}

	Py_XDECREF(self->tctx);
	self->tctx = transaction;
	Py_INCREF(transaction);

	TRACE(T_EXIT,("s", "ServerContext_setTransaction"));

	Py_INCREF(Py_None);
	return Py_None;
}

/*
** ServerContext_newTransaction
**
** Return a new transaction object
*/

static PyObject *ServerContext_newTransaction(ServerContext *self,
		PyObject *args) {
	TransactionContext *transaction = NULL;

	TRACE(T_ENTRY,("sA", "ServerContext_newTransaction", self));

	transaction = TransactionContext_alloc(self, 1);

	TRACE(T_EXIT,("sA", "ServerContext_newTransaction", transaction));
	return OBJECT(transaction);
}

/*
** Cursor_dealloc
**
** Destroy a cursor
*/

static void Cursor_dealloc(Cursor *self) {
	sword status;
	int i;

	TRACE(T_ENTRY,("sA","Cursor_dealloc",self));

	for (i = 0; i < MAX_BIND_COUNT; i++) {
		if (self->bind[i].object != NULL) {
			Py_DECREF(self->bind[i].object);
			self->bind[i].object = NULL;
		}
	}

	if (self->stmtp != NULL) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_STMT",
			self->stmtp));
		status = OCIHandleFree(self->stmtp, OCI_HTYPE_STMT);
		TRACE(T_HRETURN,("sR","OCIHandleFree",status));
		if (status != OCI_SUCCESS)
			fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
				__FILE__, __LINE__ - 3);
		self->stmtp = NULL;
	}

	/* Deallocate the per-cursor error handle */

        if (self->errhp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_ERROR",
			self->errhp));
		status = OCIHandleFree(self->errhp, OCI_HTYPE_ERROR);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
		if (status != OCI_SUCCESS) {
                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
	}

	if (self->definition != NULL) {
		Py_DECREF(self->definition);
		self->definition = NULL;
	}

	if (self->results != NULL) {
		Py_DECREF(self->results);
		self->results = NULL;
	}

	if (self->sc != NULL) {
		Py_DECREF(self->sc);
		self->sc = NULL;
	}

	PyObject_Del(self);

	TRACE(T_EXIT,("s","Cursor_dealloc"));
}

/*
** Cursor_getattr
*/

static PyObject *Cursor_getattr(Cursor *self, char *name) {
	PyObject *obj;

	TRACE(T_ENTRY,("sAS","Cursor_getattr",self,name));

	/* A cheat to get at the cursors' context */
	if (strcmp(name, "_sc_") == 0) {
		obj = OBJECT(self->sc);
		Py_INCREF(obj);
	} else
		obj = Py_FindMethod(Cursor_methods, OBJECT(self), name);

	TRACE(T_EXIT,("sA","Cursor_getattr",self,obj));

	return obj;
}

/*
** Cursor close
*/

static PyObject *Cursor_close(Cursor *self, PyObject *args) {

	TRACE(T_ENTRY,("sA","Cursor_close",self));

	/* Note that this doesn't really close the cursor, just break
	** the cycles
	*/

	if (self->definition != NULL) {
		Py_DECREF(self->definition);
		self->definition = NULL;
	}

	if (self->results != NULL) {
		Py_DECREF(self->results);
		self->results = NULL;
	}

	TRACE(T_EXIT,("s","Cursor_close"));

	Py_INCREF(Py_None);

	return Py_None;
}

/*
** Cursor bindflush
*/

static void Cursor_bindflush(Cursor *self) {
	int i;

	/* After every prepare, reset the binding pointers */
	for (i = 0; i < MAX_BIND_COUNT; i++) {

#if FREEBINDS
		/* May not be safe if statement has already been deallocated */
		if (self->bind[i].bindp != NULL) {
			sword status;
			TRACE(T_HCALL,("ssA","OCIHandleFree",
				"OCI_HTYPE_BIND", self->bind[i].bindp));
			status = OCIHandleFree(self->bind[i].bindp,
				OCI_HTYPE_BIND);
			TRACE(T_HRETURN,("sR","OCIHandleFree",status));
		}
#endif

		self->bind[i].bindp = NULL;
		self->bind[i].flags = 0;
		self->bind[i].dty = 0;	/* Clear last Datatype too */
	}
	self->flags &= ~CURSOR_NAMEBIND_NEEDSFLUSH;
}

/*
** Cursor prepare
*/

static PyObject *Cursor_prepare(Cursor *self, PyObject *args) {
	char *statement;
	int statementlen;
	sword status;

	TRACE(T_ENTRY,("sAA","Cursor_prepare",self,args));

	if (!PyArg_ParseTuple(args, "s#", &statement, &statementlen))
		return NULL;

	TRACE(T_CALL,("sASd","OCIStmtPrepare",self->stmtp, statement,
		statementlen));

	status = OCIStmtPrepare(self->stmtp, self->errhp, statement,
		statementlen, OCI_NTV_SYNTAX, OCI_DEFAULT);

	TRACE(T_RETURN,("sR","OCIStmtPrepare",status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	TRACE(T_HCALL,("sAsAs", "OCIAttrGet", self->stmtp, "OCI_HTYPE_STMT",
		&self->type, "OCI_ATTR_STMT_TYPE"));
	status = OCIAttrGet((dvoid *) self->stmtp, OCI_HTYPE_STMT,
		(dvoid *) &self->type, 0, OCI_ATTR_STMT_TYPE,
		self->errhp);
	TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, self->type));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	Cursor_bindflush(self);

	TRACE(T_EXIT,("sd","Cursor_prepare", self->type));

	return PyInt_FromLong(self->type);
}

/*
** bindObject
**
** Inspects the object to come up with a data type (dty), value start,
** value size, and indicator.  Return 0 on success, 1 on failure
**
** ctype, if not -1, will be the desired Oracle conversion type
**
** NOTE:
**
**	Bindings can have data returned to them via the RETURNING clause
**	in SQL.  For string bindings, this will illegally corrupt the
**	string data with the replacement value.  For non-string bindings
**	the value must be retrieved out from the bind array.
**
**	It would be more desirable to deterime IF the returning clause
**	is valid for this binding, and allocate space for the return
**	variable (and also copy the data) if it is a string.
*/

static int bindObject(Binding *bind, PyObject *object, int ctype) {
	int resolved = 1;

	TRACE(T_ENTRY,("sAAd","bindObject", bind, object, ctype));

	if (bind->object != NULL) {
		Py_DECREF(bind->object);
		bind->object = NULL;
	}

	bind->valuesz = 0;
	bind->object = object;
	bind->flags = 0;
	bind->pieces = 0;
	bind->u.i = 0;
	bind->alen = 0;
	bind->rcode = 0;

	Py_INCREF(object);		/* TODO do we decref on error? */

	bind->ind = 0;

	/* Here's our big hardcoded input type determination -- done
	** as a bunch of if - elses becaue it would be cumbersome to
	** be table driven (lots of tiny functions)
	**
	** Note that due to the order the checks are made, it could be
	** possiblle to misidentify something that was both a string AND
	** a long, for instance.  This shouldn't matter, since Oracle will
	** do a second conversion anyway, unless the object returns
	** different content depending on how it was accessed (ie normal
	** is "1.23" and 1.23, abnormal is "one point two three" and 1.23)
	*/

	/* Additional checks to be done:  If the object is a OUTPUT BINDING,
	** we've got to set the BIND_ARRAY_COLUMN flag
	*/

	if (object == Py_None) {
		if (bind->dty == 0)
			bind->dty = SQLT_STR;	/* Nulls are STRING if there
						** wasnt a prior type bind */
		if (bind->dty == SQLT_STR) {
			bind->valuesz = 1;
			bind->valuep = (char *) NULLSTR;

		}
		bind->ind = -1;		/* do we need more if it is null? */
	} else if (object->ob_type == &BindingArrayObjectType) {
		BindingArrayObject *bao = (BindingArrayObject *) object;

		TRACE(T_INFO,("ss","bindObject","BindingArrayObject"));

		bind->flags |= BIND_ARRAY_COLUMN;

		/*
		** NOTE:
		**
		** This is where the binding array gets put into the
		** bind structure, a bind->u.p -- very important!
		*/

		bind->u.p = object;	/* XXX Why do this re: bind->object? */
		bind->dty = bao->dty;
		if (bao->ba == NULL) {
			fprintf(stderr,"Warning: BindingArray is NULL\n");
			bind->valuesz = 0;
			bind->valuep = 0;
		} else {
			bind->valuesz = bao->ba->itemsize;
			bind->valuep = bao->ba->valuep;	/* should get ignored */
		}


		if (bao->dty == -1) resolved = 0; /* ctype can override */
	} else if (object->ob_type == &CursorType) {
		/* Cursors -- hrm */
		Cursor *cursor = (Cursor *) object;

		TRACE(T_INFO,("ss","bindObject","Cursor"));

		/* TODO Enforce that the cursor belongs to the
		** same server context
		*/

		cursor->flags |= CURSOR_INSPECT; /* Must inspect before use */
		bind->flags |= BIND_CURSOR;
		bind->u.p = object;
		bind->dty = SQLT_RSET;
		bind->valuesz = 0;
		bind->valuep = cursor->stmtp; /* TODO what happens to old? */
	} else if (object->ob_type == &OracleDateType) {
		OracleDate *odate = (OracleDate *) object;

		TRACE(T_INFO,("ss","bindObject","OracleDate"));

		bind->dty = SQLT_DAT;
		bind->valuep = &odate->ocidate;
		bind->valuesz = sizeof(odate->ocidate);
	} else if (object->ob_type == &OracleRowIDType) {
		OracleRowID *rowid = (OracleRowID *) object;

		TRACE(T_INFO,("ss","bindObject","OracleRowID"));

		bind->dty = SQLT_RDD;
		bind->valuep = &rowid->rowid;
		bind->valuesz = sizeof(rowid->rowid);
	} else if (object->ob_type == &LobLocatorType) {

		LobLocator *lobp = (LobLocator *) object;

		TRACE(T_INFO,("ss","bindObject","LobLocator"));

		bind->dty = lobp->dty;
		bind->valuep = &lobp->lobp;
		bind->valuesz = sizeof(OCILobLocator *);

	} else if (PyString_Check(object)) {
		bind->dty = SQLT_STR;
		bind->valuep = PyString_AsString(object);
		bind->valuesz = PyString_Size(object);

		TRACE(T_INFO,("ssS","bindObject","SQLT_STR", bind->valuep));

		if (ctype == -1) bind->valuesz++;	/* Add null */
	} else if (PyLong_Check(object)) {
		bind->dty = SQLT_STR;
		bind->object = PyObject_Str(object);
		/* swap out old object, now its a string */
		Py_DECREF(object);
		object = bind->object;
		bind->valuep = PyString_AsString(object);

#if (PY_MAJOR_VERSION == 1)
		/* Remove trailing L */
		bind->valuesz = PyString_Size(object);
		{
			char *v = (char *) bind->valuep;
			v[bind->valuesz - 1] = '\0';
		}
#else
		bind->valuesz = PyString_Size(object)+1;
#endif
		TRACE(T_INFO,("ssSds","bindObject","SQLT_STR",
			bind->valuep,bind->valuesz, "from long"));
	} else if (PyInt_Check(object)) {
		bind->dty = SQLT_INT;
		bind->u.l = PyInt_AsLong(object);
		bind->valuep = &bind->u.l;
		bind->valuesz = sizeof(bind->u.l);

		TRACE(T_INFO,("ssd","bindObject","SQLT_INT", bind->u.l));
	} else if (PyFloat_Check(object)) {
		bind->dty = SQLT_FLT;
		bind->u.d = PyFloat_AsDouble(object);
		bind->valuep = &bind->u.d;
		bind->valuesz = sizeof(bind->u.d);

	} else
		resolved = 0;

	/* Telling Oracle it's a different type is DANGEROUS -- use with
	** caution!
	*/

	/* TODO certain types should not be overriden, e.g. SQLT_RST */
	if (ctype != -1) {
		bind->dty = ctype;	/* Warning!  Danger, Will Robinson! */
		resolved = 1;
	}

	if (!resolved) {
		PyErr_SetString(PyExc_ValueError,"invalid data type bound");
		TRACE(T_ERROR,("ss","bindObject","invalid data type bound"));
		if (traceDump != NULL) Tracedump1();
		/*Py_DECREF(object);	 Or do we? */
		return 1;
	}

	bind->alen = bind->valuesz;

	TRACE(T_EXIT,("sRdAd","bindObject",0,bind->dty,bind->valuep,
		bind->valuesz));

	return 0;
}

/*
** dynamic Bind In
**
** Provide dynamic bindings for data longer than 64K
**
** When BindingArrays are used, we must look up the next element of the
** binding array and issue that; this would be related to the iteration
** and index.
**
*/

static sword dynamicBindIn(dvoid *context, OCIBind *bindp, ub4 iter, ub4 index,
	dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) {

	Binding *binding = (Binding *) context;
	BindingArrayObject *bao;

	TRACE(T_ENTRY,("sAdd","dynamicBindIn",context,iter,index));

	if (binding->flags & BIND_ARRAY_COLUMN) {
		/* Columnar data */
		bao = (BindingArrayObject *) binding->u.p;
		if (index >= (ub4) bao->ba->count) {
			/* Asking for more than we have will return NULL */

			TRACE(T_INFO,("ssdd", "dynamicBindIn",
				"Index out of range, using NULL", index,
				bao->ba->count));
			*bufpp = binding->valuep;
			*alenp = binding->valuesz;
			*piecep = OCI_ONE_PIECE;
			binding->ind = -1;
			*indpp = &binding->ind;

		} else {

			*bufpp = (dvoid *) ((char *) bao->ba->valuep +
				iter * bao->ba->itemsize);
			*alenp = bao->ba->alenp[iter];
			*piecep = OCI_ONE_PIECE;

			*indpp = &(bao->ba->indp[iter]);
			if (binding->dty != bao->ba->dtyp[iter]) {
				TRACE(T_ERROR,("ssdd",
					"dynamicBindIn",
					"datatype conflict", binding->dty,
					bao->ba->dtyp[iter]));
				/*
				fprintf(stderr,"Warning: datatype conflict "
					"for dynamic bind, %d declared vs "
					"%d derived\n",
					binding->dty, bao->ba->dtyp[iter]);
				*/

				/* Well, whatever WAS here isnt the type
				** that Oracle sent back, so update
				** the type now.  One hopes that it
				** didn't get sent in incorrectly.
				*/

				bao->ba->dtyp[iter] = binding->dty;
			}
		}

	} else {

		*bufpp = binding->valuep;
		*alenp = binding->valuesz;

		*piecep = OCI_ONE_PIECE;

		/* TODO BIND_CURSOR is legit input -- do we need anything?*/

		/*
		** if the binding is NULL; set the indicator column
		*/

		if (binding->object == Py_None)
			binding->ind = -1;
		else
			binding->ind = 0;
		*indpp = &binding->ind;
	}

	/* Cleanup for curesors */

	if (binding->dty == SQLT_RSET) {
		*bufpp = **(dvoid ***)bufpp;
		*alenp = 0;
		*indpp = NULL;
	}


	TRACE(T_EXIT,("sSdAdd","dynamicBindIn",*bufpp,*alenp,*indpp,
		*indpp != NULL ? **(sb2 **) indpp : 0, *piecep));

	return OCI_CONTINUE;
}

/*
** dynamic Bind Out
**
** Provide dynamic bindings for RETURNING ... data
**
** WARNING:  Called OUTSIDE of the Python interpreter lock
**
** TODO:
**
** REF CURSORs may need to be handled on a BIND OUT basis
**
*/

static sword dynamicBindOut(dvoid *context, OCIBind *bindp, ub4 iter, ub4 index,
	dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp,
	ub2 **rcodepp) {
	sword status;
	ub4   nrows;
	BindingArrayObject *bao;
	BindingArray *ba;

	Binding *binding = (Binding *) context;

	TRACE(T_ENTRY,("sAddd","dynamicBindOut",context,iter,index,
		(unsigned int) *piecep));

	bao = (BindingArrayObject *) binding->u.p;

	ba = bao->ba;

	if (ba == NULL) {
		ba = BindingArray_alloc(binding->valuesz, DEFAULT_BINDARRAY_SIZE);
		bao->ba = ba;
	}

	/* For index 0 of each iteration, we retrieve the ROWS_RETURNED to
	** allocate space to handle all of the return results.
	*/

	if (index == 0) {
		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", bindp,
			"OCI_HTYPE_BIND", &nrows,
			"OCI_ATTR_ROWS_RETURNED"));
		status = OCIAttrGet((dvoid *) bindp, OCI_HTYPE_BIND,
			(dvoid *) &nrows, 0, OCI_ATTR_ROWS_RETURNED,
			bao->sc->errhp);

		TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, nrows));

		/* Well normally we'd pitch a bitch to python but this is
		** a callback so...
		*/

		if (status != OCI_SUCCESS) {
			NoticeOCIError(bao->sc->errhp, OCI_HTYPE_ERROR);
			return status;
		}

		if (nrows == 0) nrows = 1;	/* Bah! */

		if (binding->pieces < (int) (iter + nrows))
			binding->pieces = (int) iter+nrows;
		if (ba->count < (int) (iter + nrows))
			ba->count = (int) iter+nrows;

		/* FIXME for malloc failures */

		if (ba->entries < binding->pieces)
			ba = BindingArray_realloc(ba, binding->pieces);

	}

	while (ba->entries < index) {
		ba = BindingArray_realloc(ba, ba->entries + 10);
		if (ba == NULL) return OCI_ERROR;
	}

	if (ba->count < index) ba->count = index;

	/* bufpp is the output buffer, with the following rules:
	** for a locator, it's the value of the locator, not the locator
	** address.  For a reference, it's the address of the reference.
	** From OCI manual:
	**
	** The pointer to the buffer or storage. For descriptors,
	** *bufpp contains a pointer to the descriptor. For example,
	** if you define OCILOBLocator *lobp; then you would set
	** *bufpp to lobp not *lobp. For REFs, pass the address of the
	** ref; i.e., pass &my_ref for *bufpp.
	*/

	if (ba == NULL) {
		fprintf(stderr,"Gadzooks!  dynamicBindOut has a null binding array!\n");
		return OCI_ERROR;
	}


	/* This handles the simple case (no locators or REFs) */
	*bufpp = (dvoid *) ((char *) ba->valuep +
		(index * binding->valuesz));

	/* OK, now, tell Oracle where it can set the length */
	*alenpp = (ub4 *) &ba->alenp[index];
	/* Set the size */
	ba->alenp[index] = binding->valuesz;

	/* and where it can save the indicator value */
	*indpp = &ba->indp[index];

	/* and the result code */
	*rcodepp = &ba->rcodep[index];

	TRACE(T_EXIT,("sAAdAA","dynamicBindOut", *bufpp, *alenpp,
		binding->valuesz, *indpp, *rcodepp));

	return OCI_CONTINUE;
}

/*
** Cursor bind
*/

static PyObject *Cursor_bind(Cursor *self, PyObject *obj, int pos,
	PyObject *ctypeo, char *name, int namelen) {

	int status;
	int ctype = -1;
	int crow = -1;
	ub4 nelem = 0;
	ub4 *nelemp = NULL;
	ub4 *valuep;
	ub4 valuesz;
	ub2 *rcodep;
	ub2 *alenp;
	sb2 *indp;
	sword mode = OCI_DEFAULT;

	TRACE(T_ENTRY,("sAAdASd","Cursor_bind", self, obj, pos, ctypeo, name,
		namelen));

	if (ctypeo != NULL) {
		if (PyString_Check(ctypeo))
			crow = typeByName(PyString_AsString(ctypeo));
		else if (PyInt_Check(ctypeo))
			crow = typeByType((int) PyInt_AsLong(ctypeo));
		else if (PyLong_Check(ctypeo))
			crow = typeByType((int) PyLong_AsLong(ctypeo));

		if (crow == -1) {
			PyErr_SetString(PyExc_ValueError, "invalid or unknown "
				"conversion type provided");
			return NULL;
		}
		ctype = TypeTable[crow].type; /* was ctype now use type */
	}

	pos--;

	if (bindObject(&self->bind[pos], obj, ctype))
		return NULL;

	if (self->bind[pos].valuesz > 32767 ||
		self->bind[pos].flags & BIND_ARRAY_COLUMN)
		mode = OCI_DATA_AT_EXEC;

	valuep = self->bind[pos].valuep;
	valuesz = self->bind[pos].valuesz;
	indp = 	&self->bind[pos].ind;
	alenp = &self->bind[pos].alen;
	rcodep = &self->bind[pos].rcode;

	/* LobLocators have their own indicator */
	if (obj->ob_type == &LobLocatorType) {
		LobLocator *lobp = (LobLocator *) obj;
		indp = &lobp->ind;
	}

	/* Handle array binds (still dynamic) for PL/SQL */
	if (self->bind[pos].flags & BIND_ARRAY_COLUMN) {

		BindingArrayObject *bao;
		bao = (BindingArrayObject *) self->bind[pos].u.p;
		/* If there is more than one entry, this is an array */
		if (bao->ba->entries > 1) {
			/* XXX Maybe nelem should always be zero? */
			nelem = bao->ba->entries;
			nelemp = (ub4 *) &bao->ba->count;
		}
		/*valuep = NULL;*/

		indp = bao->ba->indp;
		alenp = bao->ba->alenp;
		rcodep = bao->ba->rcodep;

		if (bao->dty == SQLT_RSET) {
			valuesz = 0;
			if (bao->ba->count == 1) {
				/* Just one cursor -- hmm -- dont dynbind */
				valuep = (dvoid *)
					bao->ba->valuep;
				nelem = 0;
				nelemp = NULL;
				mode = OCI_DEFAULT;
			}
		}

		/* OK, (hands in air) if it is an array bind, and the binding
		** is set STATIC, we DON'T do it DATA_AT_EXEC time to help to
		** avoid the problem where Oracle doesnt call our callback
		*/

	 	if ((bao->flags & BAO_STATIC && valuesz < 32767) ||
			self->type != OCI_STMT_BEGIN /* no array not pl/sql*/) {
			if (bao->flags & BAO_STATIC)
				mode = OCI_DEFAULT;
			nelem = 0;
			nelemp = NULL;	/* ugh */
		}
	}



	/*
	** this is either a bind by name or a bind by pos; 0 by pos, nonzero
	** by name
	*/
	if (namelen == 0) {


		TRACE(T_CALL,("sAdAddd","OCIBindByPos", self->stmtp,pos+1,
			valuep, valuesz,
			self->bind[pos].dty, *indp));

		TRACE((T_CALL | T_INFO),("ssdAdd","OCIBindByPos","supplemental",
			nelem, nelemp, mode, *alenp));

		TRACE((T_CALL | T_INFO),("ssdAAA","OCIBindByPos","arrays",
			nelem, indp, alenp, rcodep));


		status=OCIBindByPos(
			self->stmtp,			/* Statment	*/
			&self->bind[pos].bindp,		/* Bind handle	*/
			self->errhp,  			/* Error handle	*/
			pos+1,				/* Position	*/
			valuep,				/* valuep	*/
			valuesz,			/* value_sz	*/
			self->bind[pos].dty,		/* data type	*/
			indp,				/* indp		*/
			alenp,				/* alenp	*/
			rcodep,				/* rcodep	*/
			nelem,				/* maxarr_len	*/
			nelemp, 			/* curelep	*/
			mode );				/* mode		*/

		TRACE(T_RETURN,("sR", "OCIBindByPos", status));
	} else {
		/* Darn, only 7 variables allowed in a trace */
		TRACE(T_CALL,("sASAddd","OCIBindByName", self->stmtp, name,
			valuep, valuesz,
			self->bind[pos].dty, self->bind[pos].ind));

		TRACE((T_CALL | T_INFO),("ssddAdd","OCIBindByName",
			"supplemental", pos, nelem, nelemp, mode, *alenp));

		TRACE((T_CALL | T_INFO),("ssdAAA","OCIBindByName","arrays",
			nelem, indp, alenp, rcodep));

		status=OCIBindByName(
			self->stmtp,			/* Statment	*/
			&self->bind[pos].bindp,		/* Bind handle	*/
			self->errhp,			/* Error handle	*/
			name,				/* Placeholder	*/
			namelen,			/* Placeh_len	*/
			valuep,				/* valuep	*/
			valuesz,			/* value_sz	*/
			self->bind[pos].dty,		/* data type	*/
			indp,				/* indp		*/
			alenp,				/* alenp	*/
			rcodep,				/* rcodep	*/
			nelem,				/* maxarr_len	*/
			nelemp, 			/* curelep	*/
			mode );				/* mode		*/

		TRACE(T_RETURN,("sR", "OCIBindByName", status));
	}

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	if (self->bind[pos].flags & BIND_ARRAY_COLUMN && mode != OCI_DEFAULT) {
		TRACE(T_CALL,("sA","OCIBindDynamic",&self->bind[pos]));

		status = OCIBindDynamic(self->bind[pos].bindp,
			self->errhp, (dword *) &self->bind[pos],
			(OCICallbackInBind) dynamicBindIn,
			(dword *) &self->bind[pos],
			(OCICallbackOutBind) dynamicBindOut);

		TRACE(T_RETURN,("sRsAA","OCIBindDynamic",status, "OUT",
			self->bind[pos].bindp,&self->bind[pos]));

		if (status != OCI_SUCCESS)
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);


	} else if (self->bind[pos].valuesz > 32767) { /* Whoops ! too big */
		self->bind[pos].pieces = (self->bind[pos].valuesz +32766)
			/  32767;

		TRACE(T_CALL,("sA","OCIBindDynamic",&self->bind[pos]));

		status = OCIBindDynamic(self->bind[pos].bindp,
			self->errhp, (dword *) &self->bind[pos],
			(OCICallbackInBind) dynamicBindIn, NULL, NULL);

		TRACE(T_RETURN,("sRsAA","OCIBindDynamic",status, "LONG",
			self->bind[pos].bindp,&self->bind[pos]));

		if (status != OCI_SUCCESS)
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
	} else if (self->bind[pos].flags & BIND_ARRAY_COLUMN
		&& self->type == OCI_STMT_BEGIN) { /*static bind*/
		BindingArrayObject *bao = self->bind[pos].u.p;
		TRACE(T_CALL,("sd","OCIBindArrayOfStruct", bao->ba->itemsize));

		status = OCIBindArrayOfStruct(self->bind[pos].bindp,
			self->errhp,
			bao->ba->itemsize,	/* pv	*/
			sizeof(ub2),		/* ind	*/
			sizeof(ub4),		/* rl	*/
			sizeof(ub2)		/* rc	*/
		);

		TRACE(T_CALL,("sd","OCIBindArrayOfStruct", status));

		if (status != OCI_SUCCESS)
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
	}


	Py_INCREF(Py_None);

	TRACE(T_EXIT,("s","Cursor_bind"));

	return Py_None;
}

/*
** Cursor_bindbypos
**
*/

static PyObject *Cursor_bindbypos(Cursor *self, PyObject *args) {
	PyObject *obj;
	PyObject *ctypeo = NULL;
	int pos;
	PyObject *rval;

	TRACE(T_ENTRY,("sAA","Cursor_bindbypos",self,args));

	if (!PyArg_ParseTuple(args,"iO|O:bindbypos", &pos, &obj, &ctypeo))
		return NULL;

	if (pos < 1 || pos > MAX_BIND_COUNT) {
		PyErr_SetString(PyExc_ValueError,"bind position out of range");
		return NULL;
	}

	rval = Cursor_bind(self, obj, pos, ctypeo, NULL, 0);

	TRACE(T_EXIT,("sA","Cursor_bindbypos", rval));

	return rval;
}

/*
** Cursor_bindbyname
**
*/

static PyObject *Cursor_bindbyname(Cursor *self, PyObject *args) {
	PyObject *obj;
	char *name = NULL;
	int namelen;
	PyObject *ctypeo = NULL;
	int pos;
	PyObject *rval;
	Cursor *cursor;

	TRACE(T_ENTRY,("sAA","Cursor_bindbyname",self,args));

	if (!PyArg_ParseTuple(args,"s#O|O:bindbyname", &name, &namelen,
		&obj, &ctypeo)) return NULL;

	/* We cheat by computing pos from the rear of the BIND */

	cursor = (Cursor *) self;

	if (cursor->flags & CURSOR_NAMEBIND_NEEDSFLUSH)
		Cursor_bindflush(cursor);

	pos = MAX_BIND_COUNT - 1;

	while(pos >= 1 && cursor->bind[pos-1].bindp != NULL) pos--;

	if (pos < 1) {
		PyErr_SetString(PyExc_ValueError,"bind positions exhausted");
		TRACE(T_EXIT,("sA","Cursor_bindbyname", NULL));
		return NULL;
	}

	rval = Cursor_bind(self, obj, pos, ctypeo, name, namelen);

	TRACE(T_EXIT,("sA","Cursor_bindbyname", rval));

	return rval;
}


static int Cursor_getdesc(Cursor *self) {

	PyObject *list;
	PyObject *tuple;
	sword status;
	OCIParam *paramd = NULL;
	char	*colname;
	ub2	dtype;
	int	type;
	int	size;
	int	precision;
	int	scale;
	int	nullok;
	ub4	pos = 1;
	ub4	word;
	ub1	itype;
	sb1	stype;
	ub2	colnamelen;
/*	ub1 	*bytep = (ub1 *) &word;*/
/*	sb1 	*sbytep = (sb1 *) &word;*/
	ub2 	*halfp = (ub2 *) &word;
	int 	lastresult = 0;

	TRACE(T_ENTRY,("sA","Cursor_getdesc", self));

	list = Py_BuildValue("[]");
	status = OCI_SUCCESS;

	for (pos = 1; status == OCI_SUCCESS; pos++) {

		if (paramd != NULL) {
			TRACE(T_HCALL,("ssA","OCIDescriptorFree","OCI_DTYPE_PARAM",
				paramd));
			status = OCIDescriptorFree(paramd, OCI_DTYPE_PARAM);
			TRACE(T_HRETURN,("sR","OCIDescriptorFree", status));
			paramd = NULL;
		}

		TRACE(T_HCALL,("sAsAd", "OCIParamGet", self->stmtp,
			"OCI_HTYPE_STMT", &paramd, pos));
		status = OCIParamGet(self->stmtp, OCI_HTYPE_STMT,
			self->errhp, (dvoid **) &paramd, pos);
		TRACE(T_HRETURN,("sRA", "OCIParamGet", status, paramd));

		if (status != OCI_SUCCESS) break;

		lastresult = pos;

		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", paramd,
			"OCI_DTYPE_PARAM", &dtype, "OCI_ATTR_DATA_TYPE"));
		status = OCIAttrGet((dvoid *) paramd, OCI_DTYPE_PARAM,
			(dvoid *) &dtype, 0, OCI_ATTR_DATA_TYPE,
			self->errhp);
		TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, dtype));

		type = dtype;

		if (status != OCI_SUCCESS) break;

		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", paramd,
			"OCI_DTYPE_PARAM", &dtype, "OCI_ATTR_DATA_SIZE"));
		status = OCIAttrGet((dvoid *) paramd, OCI_DTYPE_PARAM,
			(dvoid *) &dtype, 0, OCI_ATTR_DATA_SIZE,
			self->errhp);
		TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, dtype));
		size = dtype;

		if (status != OCI_SUCCESS) break;

		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", paramd,
			"OCI_DTYPE_PARAM", &itype, "OCI_ATTR_PRECISION"));
		status = OCIAttrGet((dvoid *) paramd, OCI_DTYPE_PARAM,
			(dvoid *) &word, 0, OCI_ATTR_PRECISION,
			self->errhp);
		itype = *halfp;		/* SPARC alignment workaorund */
		TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, itype));
		precision = itype;

		if (status != OCI_SUCCESS) break;

		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", paramd,
			"OCI_DTYPE_PARAM", &stype, "OCI_ATTR_SCALE"));
		status = OCIAttrGet((dvoid *) paramd, OCI_DTYPE_PARAM,
			(dvoid *) &stype, 0, OCI_ATTR_SCALE,
			self->errhp);
		TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, stype));
		scale = stype;

		if (status != OCI_SUCCESS) break;

		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", paramd,
			"OCI_DTYPE_PARAM", &itype, "OCI_ATTR_IS_NULL"));
		status = OCIAttrGet((dvoid *) paramd, OCI_DTYPE_PARAM,
			(dvoid *) &itype, 0, OCI_ATTR_IS_NULL,
			self->errhp);
		TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, itype));
		nullok = (itype != 0);

		if (status != OCI_SUCCESS) break;

		TRACE(T_HCALL,("sAsAs", "OCIAttrGet", paramd,
			"OCI_DTYPE_PARAM", &colname, "OCI_ATTR_NAME"));
		status = OCIAttrGet((dvoid *) paramd, OCI_DTYPE_PARAM,
			(dvoid **) &colname,
			(dvoid *) &word, OCI_ATTR_NAME, self->errhp);
		colnamelen = (ub2) word;	/* SPARC alignment */
		TRACE(T_HRETURN,("sRS", "OCIAttrGet", status, colname));

		if (status != OCI_SUCCESS) break;

		/*
		** Python DB API 2.0 defines the result as
		**
		** (name, type_code, display_size, internal_size,
		** 	precision, scale, null_ok)
		*/

		if (type == SQLT_LBI || type == SQLT_LNG) {
			self->flags |= LONG_COLUMN;
			self->longcol = pos;
		}

		if (type == SQLT_CLOB || type == SQLT_BLOB ||
			type == SQLT_CFILE || type == SQLT_CFILE)
			self->flags |= LOB_COLUMN;

		tuple = Py_BuildValue("(s#iiiiii)", colname, colnamelen,
			(int) type, size, size, precision, scale, nullok);

		TRACE(T_RESULT,("sdSdddd","Cursor_getdesc", pos, colname,
			type, size, precision, scale));

		if (PyList_Append(list, tuple)) {
			TRACE(T_ERROR,("ss","Cursor_getdesc",
				"PyList_Append failed to append"));
			return -1;
		}
		Py_DECREF(tuple);

	}

	if (paramd != NULL) {
		TRACE(T_HCALL,("ssA","OCIDescriptorFree","OCI_DTYPE_PARAM",
			paramd));
		status = OCIDescriptorFree(paramd, OCI_DTYPE_PARAM);
		TRACE(T_HRETURN,("sR","OCIDescriptorFree", status));
		paramd = NULL;
	}

	if (self->definition != NULL) {
		Py_DECREF(self->definition);
	}
	self->definition = list;
	self->current = -1;	/* Toss any prior results */
	self->count = 0;
	self->batchct = -1;
	self->flags &= ~CURSOR_INSPECT;
	if (self->results != NULL) {
		Py_DECREF(self->results);
		self->results = NULL;
	}

	if (lastresult == 0) {
		self->flags |= NO_RESULT;
		Py_DECREF(self->definition);
		Py_INCREF(Py_None);
		self->definition = Py_None;
	}

	TRACE(T_EXIT,("s","Cursor_getdesc"));

	return 0;
}

/*
** Cursor execute
*/

static PyObject *Cursor_execute(Cursor *self, PyObject *args) {
	sword status;
	ub4 iters = 1;
	ub4 rowoff = 0;
	int rowlimit = -1;
	int rcount;

	TRACE(T_ENTRY,("sAA", "Cursor_execute", self, args));

	if (!PyArg_ParseTuple(args, "|i", &rowlimit)) return NULL;

	/* For Select statements, iters should be zero if we don't know
	** how many rows we want returned.  For now, we just take 1 row.
	** We *should* make this number quite a bit larger to handle
	** queries more efficiently but this requires the corresponding
	** define call to have allocated buffer space for the result.
	*/

	if (self->type == OCI_STMT_SELECT) iters = 0;
	else if (self->type != OCI_STMT_BEGIN) {
		/* Check for a binding array in the first bind position,
		** and use its count, regardless of the fact that the binds
		** may not be equal in elements
		*/

		if (self->bind[0].flags & BIND_ARRAY_COLUMN) {
			BindingArrayObject *bao;
			bao = (BindingArrayObject *) self->bind[0].u.p;

			iters = bao->ba->count;
		}
	}

	self->flags = 0;	/* Reset the cursor flags */
	self->longcol = 0;

	/* Allow the caller to specify the maximum number of rows */
	if (rowlimit > -1 && (sb4) iters > rowlimit) iters = rowlimit;

	TRACE(T_CALL,("sd","OCIStmtExecute",iters));

	Py_BEGIN_ALLOW_THREADS

	status = OCIStmtExecute(self->sc->svchp, self->stmtp, self->errhp,
		iters, rowoff, NULL, NULL, OCI_DEFAULT);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR","OCIStmtExecute",status));

	/* FIXME we need to reflect the warning better */
	if (status == OCI_SUCCESS_WITH_INFO) {
		NoticeOCIError(self->errhp, OCI_HTYPE_ERROR);
		status = OCI_SUCCESS;
	}

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	/* Now get our new description */
	if (Cursor_getdesc(self) < 0) return NULL;

	/* Now get the # of rows affected */

	TRACE(T_HCALL,("sAsAs", "OCIAttrGet", self->stmtp,
		"OCI_HTYPE_STMT", &rcount, "OCI_ATTR_ROW_COUNT"));
	status = OCIAttrGet((dvoid *) self->stmtp, OCI_HTYPE_STMT,
		(ub4 *) &rcount, 0, OCI_ATTR_ROW_COUNT,
		self->errhp);
	TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, rcount));

	if (status == OCI_SUCCESS)
		self->batchct = rcount;
	else
		self->batchct = -1;

	self->flags |= CURSOR_NAMEBIND_NEEDSFLUSH;	/* Flush named binds */

	/* TODO
	**
	** Instead of checking for CURSOR_INSPECT and picking up the
	** description later, figure out a way to know if we need to
	** go pick up any new descriptions right now.
	*/


	TRACE(T_EXIT,("sd", "Cursor_execute", self->type));

	return PyInt_FromLong(self->type);

}

/*
** Cursor describe
**
** Generate a list of the types of the return values of the statement
*/

static PyObject *Cursor_describe(Cursor *self, PyObject *args) {
	PyObject *list;

	TRACE(T_ENTRY,("sAA", "Cursor_describe", self, args));

	/* bindObject may have bound this cursor, in which case we
	** must refetch the description
	*/
	if (self->flags & CURSOR_INSPECT)
		Cursor_getdesc(self);

	list = self->definition;

	if (list == NULL) {
		TRACE(T_ERROR,("ss","Cursor_describe","description is NULL"));
		PyErr_SetString(ProgrammingErrorObject,
			"Describe invalid prior to statement execution");
		return NULL;
	}
	Py_INCREF(list);

	TRACE(T_EXIT,("s", "Cursor_describe"));

	return list;
}

/*
** Dynamic Fetch
**
** A callback function to manage the fetching of LONG RAW columns
**
** WARNING:
**
** The python global interpreter lock has been RELEASED at this point, so
** calling Python functions is unsafe.
**
** TODO: Investigate thread-safety-ness of PyMem_* functions
*/

static sb4 dynamicFetch(dvoid *context, OCIDefine *defnp,
	ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp,
	ub2 **rcodep) {

	LongFetch *lf = (LongFetch *) context;
	char *buf;

	TRACE(T_ENTRY,("sAAd", "dynamicFetch", context, *bufpp, *piecep));

	buf = longFetchAlloc(lf, FETCHBUCKETSIZE);

	if (buf != NULL)  {
		*bufpp = (dvoid *) buf;
		*alenpp = (ub4 *) &lf->length[lf->pieces - 1];
	} else  {
		TRACE(T_ERROR,("ss", "dynamicFetch", "unable to allocate "
			"long fetch buffer"));
		return OCI_ERROR;
	}

	if (*piecep == OCI_ONE_PIECE) *piecep = OCI_FIRST_PIECE;

	lf->ind = **alenpp;
	lf->rcode = 0;
	*indpp = (dvoid *) &lf->ind;
	*rcodep = (ub2 *) &lf->rcode;

	TRACE(T_EXIT,("sAdddd", "dynamicFetch", *bufpp, *piecep, **alenpp,
		 lf->ind, lf->rcode));

	return OCI_CONTINUE;
}

/*
** Cursor ResultSet
**
** Returns a list of all result columns
*/

static PyObject *Cursor_ResultSet(Cursor *self, int count) {
	PyObject *list;
	ResultSet *rs;
	int status;
	int i;
	sword mode = OCI_DEFAULT;
	dvoid *valuep;
	ub4 width;
	LongFetch *lf;

	TRACE(T_ENTRY,("sAd", "Cursor_ResultSet", self, count));

	if (self->definition == NULL) {
		TRACE(T_ERROR,("ss","Cursor_ResultSet","description is NULL"));
		PyErr_SetString(ProgrammingErrorObject,
			"cursor description is None");
		return NULL;
	}

	self->batchsz = count;

	if ((list = Py_BuildValue("[]")) == NULL) {
		TRACE(T_ERROR,("ss","Cursor_ResultSet",
			"PyBuildValue returned NULL"));
		return NULL;
	}

	for (i = 1; i <= PyList_Size(self->definition); i++) {
		mode = OCI_DEFAULT;
		if ((rs = (ResultSet *) ResultSet_alloc(self, i, count))
			== NULL) {

			Py_DECREF(list);
			TRACE(T_ERROR,("ss","Cursor_ResultSet",
				"ResultSetAlloc returned NULL"));
			return NULL;
		}

		valuep = rs->valuep;
		width = rs->width;
		rs->fetchResultCode = OCI_SUCCESS;

		if (self->flags & LONG_COLUMN && (char) i == self->longcol) {
			mode = OCI_DYNAMIC_FETCH;
			lf = (LongFetch *) rs->valuep;
			longFetchInit(lf);
			/*valuep = NULL;*/
			width = 0x7FFFFFFF; /* Max unsigned long */
			rs->indp = &lf->ind; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
			rs->rcodep = &lf->rcode; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
		}

		TRACE(T_CALL,("sdAddd", "OCIDefineByPos", i, valuep, width,
			rs->cdty, mode));

		/* Now bind the result set */
		/*
		Docu from OCI:
		indp (IN/OUT), alenp (IN/OUT), rcodep (OUT): Ignored for dynamic binds.
		*/
		status = OCIDefineByPos(self->stmtp, &(rs->defnp),
			self->errhp, i, valuep, width,
			rs->cdty, (dvoid *) rs->indp,
			rs->rlenp, rs->rcodep, mode);

		TRACE(T_RETURN,("sR", "OCIDefineByPos", status));

		if (status != OCI_SUCCESS) {
			Py_DECREF(rs);
			Py_DECREF(list);
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		}

		if (self->flags & LONG_COLUMN && (char) i == self->longcol) {

			TRACE(T_CALL,("sA", "OCIDefineDynamic", rs->valuep));

			status = OCIDefineDynamic(rs->defnp, self->errhp,
				(dvoid *) rs->valuep,
				(OCICallbackDefine) dynamicFetch);

			TRACE(T_RETURN,("sR", "OCIDefineDynamic", status));

			if (status != OCI_SUCCESS) {
				Py_DECREF(rs);
				Py_DECREF(list);
				return RaiseOCIError(self->errhp,
					OCI_HTYPE_ERROR);
			}
		}

		PyList_Append(list, OBJECT(rs));
		Py_DECREF(rs); /* Now that its in the list ... */
	}

	if (self->results != NULL) {
		Py_DECREF(self->results);
	}

	self->results = list;
	self->current = 0;

	Py_INCREF(Py_None);

	TRACE(T_EXIT,("s","Cursor_ResultSet"));

	return Py_None;
}

/*
** Cursor Fetch
**
** Fetch N results from a cursor
*/

static PyObject *Cursor_fetch(Cursor *self, PyObject *args) {
	int count = 1;
	ub4 rcount;
	int status;
	ub2 *rlenp;
	ResultSet *rs;
	int i;
	int j;
	int resetlong = 0;
	int releaseresults = 0;
	PyObject *answer;

	TRACE(T_ENTRY,("sAA", "Cursor_fetch", self, args));

	/* bindObject may have bound this cursor, in which case we
	** must refetch the description
	*/
	if (self->flags & CURSOR_INSPECT)
		Cursor_getdesc(self);

	if (!PyArg_ParseTuple(args, "|i", &count)) return NULL;

	TRACE(T_ARGS,("sd", "Cursor_fetch", count));

	if (count < 1 || count > 200) {
		PyErr_SetString(PyExc_ValueError, "Count out of range");
		return NULL;
	}

	if (self->flags & LONG_COLUMN) {
		count = 1;		/* Should we raise an error? */

		if (self->current != -1)
			resetlong = 1;
	}


	if (self->flags & NO_RESULT) {
		TRACE((T_PROGRAM|T_ERROR),("ss","Cursor_fetch",
			"no result set from execute"));
		PyErr_SetObject(ProgrammingErrorObject,
			Py_BuildValue("s",
			"No results available from last execute operation"));
		return NULL;
	}

	if (self->current == -1)
		if (Cursor_ResultSet(self, count) == NULL) return NULL;

	/* Set the result widths */

	for (i = 0; i < PyList_Size(self->results); i++) {
		if ((rs = (ResultSet *) PyList_GetItem(self->results, i))
						== NULL) return NULL;

		rlenp = rs->rlenp;
		for (j = 0; j < rs->size; j++) {
			*rlenp = (ub2) rs->width;
			rlenp++;
		}

		if (resetlong == 1 && (i+1) == self->longcol) {
			LongFetch *lf = (LongFetch *) rs->valuep;
			longFetchRelease(lf);
			longFetchInit(lf);
			rs->indp = &lf->ind; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
			rs->rcodep = &lf->rcode; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
		}
	}


	TRACE(T_CALL,("sd", "OCIStmtFetch", count));

	Py_BEGIN_ALLOW_THREADS

	/*
	** NB: A fetch of length 0 will cancel the cursor
	**
	** Do we *need* to cancel the cursor when the Cursor is
	** deallocated or will Oracle figure it out when the statement
	** handle is released?
	*/

	status = OCIStmtFetch(self->stmtp, self->errhp, count,
		OCI_FETCH_NEXT, OCI_DEFAULT);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCIStmtFetch", status));

	/*
	** Copy the fetch status into each result
	*/
	if (status == OCI_SUCCESS_WITH_INFO) {
		for (i = 0; i < PyList_Size(self->results); i++) {
			rs = (ResultSet *) PyList_GetItem(self->results, i);
			rs->fetchResultCode=status;
		}
	}
	if (status == OCI_SUCCESS) {
		for (i = 0; i < PyList_Size(self->results); i++) {
			rs = (ResultSet *) PyList_GetItem(self->results, i);
			rs->fetchResultCode=status;
		}
	}
	TRACE(T_VERBOSE,("sd", "Cursor_fetch status", status));

	if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) {
		text buff[OCI_ERROR_MAXMSG_SIZE];
		sb4	errcode;

		OCIErrorGet(self->errhp, 1, NULL, &errcode, buff,
			sizeof(buff), OCI_HTYPE_ERROR);
		/* Oracle errors meaning "end of fetch"
		** 1403 is
		** 1002 is fetch out of sequence
		*/

		TRACE(T_OERROR,("sdS","OCIStmtFetch",errcode,buff));

		if (errcode != 1403 && errcode != 1002) {/* Not End of data */
			Py_DECREF(self->results);
			self->results = NULL;
			self->current = -1;
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		}
		releaseresults = 1;
	}

	TRACE(T_HCALL,("sAsAs", "OCIAttrGet", self->stmtp,
		"OCI_HTYPE_STMT", &rcount, "OCI_ATTR_ROW_COUNT"));
	status = OCIAttrGet((dvoid *) self->stmtp, OCI_HTYPE_STMT,
		(ub4 *) &rcount, 0, OCI_ATTR_ROW_COUNT,
		self->errhp);
	TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, rcount));

	if (status == OCI_SUCCESS) {
		if (self->batchct == -1) self->batchct = 0;
		self->count = rcount - self->batchct;
		self->batchct = rcount;
	} else
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	answer = self->results;
	if (!releaseresults)
		Py_INCREF(answer);
	else {
		self->results = NULL;	/* Our caller gets our only copy*/
		self->current = -1;
	}

	TRACE(T_EXIT,("sA", "Cursor_fetch", answer));

	return answer;

}

/*
** Cursor_setPrefetch
**
** Set the prefetch handles on the cursor
*/


static PyObject *Cursor_setPrefetch(Cursor *self, PyObject *args) {

	PyObject *result;
	ub4 records = 1;
	ub4 size = 0;
	sword status;

	TRACE(T_ENTRY,("sAA", "Cursor_setPrefetch", self, args));

	if (!PyArg_ParseTuple(args, "i|i", &records, &size)) return NULL;


	TRACE(T_HCALL,("ssAsd", "OCIAttrSet", "OCI_HTYPE_STMT",
		self->stmtp, "OCI_ATTR_PREFETCH_ROWS", records));
	status = OCIAttrSet(self->stmtp, OCI_HTYPE_STMT,
		&records, sizeof(ub4), OCI_ATTR_PREFETCH_ROWS, self->errhp);
	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	TRACE(T_HCALL,("ssAsd", "OCIAttrSet", "OCI_HTYPE_STMT",
		self->stmtp, "OCI_ATTR_PREFETCH_MEMORY", size));
	status = OCIAttrSet(self->stmtp, OCI_HTYPE_STMT,
		&size, sizeof(ub4), OCI_ATTR_PREFETCH_MEMORY, self->errhp);
	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	Py_INCREF(Py_None);
	result = Py_None;

	TRACE(T_EXIT,("sA", "Cursor_setPrefetch", result));
	return result;

}

/*
** Cursor_rowcount
**
** Get the batch count set by exec() and fetch()
*/

static PyObject *Cursor_rowcount(Cursor *self, PyObject *args) {
	PyObject *result;

	TRACE(T_ENTRY,("sA","Cursor_rowcount", self));

	result = PyInt_FromLong((long) self->batchct);

	TRACE(T_EXIT,("sAd", "Cursor_rowcount", result, self->batchct));

	return result;
}


/*
** typeByName
**
** Returns the row number of an oracle type specified by name
** Returns -1 if not found.
*/

static int typeByName(char *name) {
	int i;

	i = 0;

	while (TypeTable[i].name) {
		if (strcmp(name, TypeTable[i].name) == 0) return i;

		i++;
	}

	return -1;
}

/*
** typeByType
**
** Returns the row number of an oracle type specified by type code.
** Returns -1 if not found.
*/

static int typeByType(int type) {
	int i;

	i = 0;

	while (TypeTable[i].name) {
		if (TypeTable[i].type == type) return i;

		i++;
	}

	return -1;
}

/*
** TypeTable_lookup
**
** Provides an external interface to the builtin type table, returning
** either:
** 	The whole type table (no arguments)
**	the numeric type (for matching type name)
**	the string type (for matching type number)
*/

static PyObject *TypeTable_lookup(PyObject *self, PyObject *args) {
	PyObject *result = NULL;
	PyObject *key = NULL;
	int i;
	int l;

	TRACE(T_ENTRY,("s", "TypeTable_lookup"));

	if (!PyArg_ParseTuple(args, "|O", &key)) return NULL;

	if (key == NULL) { /* Format the whole type table */
		result = Py_BuildValue("[]");
		for (i=0; TypeTable[i].name; i++)
		    PyList_Append(result,
			Py_BuildValue("(si)", TypeTable[i].name,
			TypeTable[i].type));

	} else {	/* Maybe its a string object? */
		if (PyString_Check(key)) {
			l = typeByName(PyString_AsString(key));
			if (l > -1)
				result = Py_BuildValue("i", TypeTable[l].type);
		} else if (PyInt_Check(key))  {
			l = typeByType(PyInt_AsLong(key));
			if (l > -1)
				result = Py_BuildValue("s", TypeTable[l].name);
		}
	}

	if (result == NULL) {
		Py_INCREF(Py_None);
		result = Py_None;
	}

	TRACE(T_EXIT,("sA", "TypeTable_lookup", result));
	return result;
}

/*
** ResultSet alloc
**
** Allocates 1 column to hold count results
**
*/

static PyObject *ResultSet_alloc(Cursor *cursor, int column, int count) {

	ResultSet *self;
	PyObject  *definitionItem;
	int	width;
	int	type;
	int	ctype;
	int	volume;
	int	modo;
	int	indo;
	int	rleno;
	int	rcodo;
	int 	r;

	TRACE(T_ENTRY,("sAdd", "ResultSet_alloc", cursor, column, count));

	if (cursor->definition == NULL) {
		PyErr_SetString(PyExc_AssertionError,
			"Result set cannot be defined when no statement "
			"has been executed");
		return NULL;
	}

	if (column < 1 || column > PyList_Size(cursor->definition)) {
		PyErr_SetString(PyExc_IndexError, "ResultSet column "
			"is out of range");
		return NULL;
	}

	if (count < 1 || count > MAX_RESULT_SET_COUNT) {
		PyErr_SetString(PyExc_IndexError, "ResultSet count "
			"is out of range");
		return NULL;
	}

	if ((definitionItem = PyList_GetItem(cursor->definition, column -1))
		== NULL) return NULL;

	type = PyInt_AsLong(PyTuple_GetItem(definitionItem, 1));

	if ((r = typeByType(type)) == -1) {
		PyErr_SetString(PyExc_TypeError, "Invalid type on column for "
			"ResultSet_alloc");
		return NULL;
	}

	/* Width is the Maximum size in bytes to hold the answer */
	width = PyInt_AsLong(PyTuple_GetItem(definitionItem, 2));

	/*
	** Important type fakeout here!
	**
	** Certain types (namely SQLT_NUM) are very awkward to deal
	** with.  Thus, if the TypeTable entry ctype is != type, we
	** use its ctype and width; the appropriate convertOut handler
	** will expect the data in the converted format
	*/

	if (TypeTable[r].ctype != -1) {
		width = TypeTable[r].cwidth;
		ctype = TypeTable[r].ctype;
	} else
		ctype = type;

	if ((self = PyObject_NEW(ResultSet, &ResultSetType)) == NULL)
		return NULL;

	self->cursor = cursor;
	Py_INCREF(cursor);

	self->definition = cursor->definition;
	Py_INCREF(cursor->definition);

	self->size = count;
	self->width = width;			/* There might be padding */
	self->valuesz = width;
	self->dty = (ub2) type;
	self->cdty = (ub2) ctype;
	self->column = column;
	self->fetchResultCode = OCI_SUCCESS;

	volume = self->width * count;		/* Base volume 		*/
	if (volume % PADSIZE != 0) volume += PADSIZE - (volume % PADSIZE);

	modo = volume;				/* Offset for now	*/
	volume += sizeof(char) * count;
	if (volume % PADSIZE != 0) volume += PADSIZE - (volume % PADSIZE);

	indo = volume;				/* Offset for now	*/
	volume += sizeof(sb2) * count;
	if (volume % PADSIZE != 0) volume += PADSIZE - (volume % PADSIZE);

	rleno= volume;				/* Offset for now	*/
	volume += sizeof(ub2) * count;
	if (volume % PADSIZE != 0) volume += PADSIZE - (volume % PADSIZE);

	rcodo= volume;				/* Offset for now	*/
	volume += sizeof(ub2) * count;
	if (volume % PADSIZE != 0) volume += PADSIZE - (volume % PADSIZE);

	if ((self->valuep = PYMALLOC(volume)) == NULL)
		return PyErr_NoMemory();

	self->modified	= (char *) ((unsigned long) self->valuep +
		(unsigned long) modo);
	self->indp	= (sb2 *) ((unsigned long) self->valuep +
		(unsigned long) indo);
	self->rlenp	= (ub2 *) ((unsigned long) self->valuep +
		(unsigned long) rleno);
	self->rcodep	= (ub2 *) ((unsigned long) self->valuep +
		(unsigned long) rcodo);

	/* LOB Locators */

	if (ctype == SQLT_BLOB || ctype == SQLT_CLOB ||
		ctype == SQLT_BFILE || ctype == SQLT_CFILE) {
		OCILobLocator **lobpp;
		int i;
		sword status;
		char *lobname;
		ub4 lobtype;

		SETLOBTYPE(ctype, lobname, lobtype);

		lobpp = (OCILobLocator **) self->valuep;
		for (i = 0; i < count; i++) {
			*lobpp = NULL;

			TRACE(T_HCALL,("sAs", "OCIDescriptorAlloc", lobpp,
				lobname));
			status = OCIDescriptorAlloc(cursor->sc->envhp,
				(dvoid **) lobpp,
				lobtype, 0, NULL);
			TRACE(T_HRETURN,("sRA", "OCIDescriptorAlloc",status,
				*lobpp));

			if (status != OCI_SUCCESS)
				return RaiseOCIError(cursor->sc->envhp,
					OCI_HTYPE_ENV);

			lobpp++;

		}
	}

	if (ctype == SQLT_RDD) {
		OCIRowid **rowidpp;
		int i;
		sword status;

		rowidpp = (OCIRowid **)self->valuep;

		for (i = 0; i < count; i++) {
			*rowidpp = NULL;

			TRACE(T_HCALL,("sAs", "OCIDescriptorAlloc", rowidpp,
				"OCI_DTYPE_ROWID"));
			status = OCIDescriptorAlloc(cursor->sc->envhp,
				(dvoid **) rowidpp,
				OCI_DTYPE_ROWID, 0, NULL);
			TRACE(T_HRETURN,("sRA", "OCIDescriptorAlloc",status,
				*rowidpp));

			if (status != OCI_SUCCESS)
				return RaiseOCIError(cursor->sc->envhp,
					OCI_HTYPE_ENV);

			rowidpp++;

		}
	}


	/* REF CURSORS and result sets */

	if (ctype == SQLT_RSET) {
		OCIStmt **stmtp;
		int i;
		sword status;

		stmtp = (OCIStmt **) self->valuep;
		for (i = 0; i < count; i++) {

			TRACE(T_HCALL,("sAs", "OCIHandleAlloc", stmtp,
				"OCI_HTYPE_STMT"));
			status = OCIHandleAlloc(cursor->sc->envhp,
				(dvoid **) stmtp, OCI_HTYPE_STMT, 0, 0);
			TRACE(T_HRETURN,("sRA", "OCIHandleAlloc", status,
				*stmtp));

			if (status != OCI_SUCCESS) {

				return RaiseOCIError(cursor->sc->envhp,
					OCI_HTYPE_ENV);
			}


			stmtp++;
		}
	}

	TRACE(T_EXIT,("sA", "ResultSet_alloc", self));

	return OBJECT(self);
}

/*
** ResultSet_dealloc
*/

static void ResultSet_dealloc(ResultSet *self) {

	TRACE(T_ENTRY,("sA", "ResultSet_dealloc", self));

	if (self->cursor != NULL) {
		Py_DECREF(self->cursor);
		self->cursor = NULL;
	}

	if (self->definition != NULL) {
		Py_DECREF(self->definition);
		self->definition = NULL;
	}



	if (self->cdty == SQLT_LBI || self->cdty == SQLT_LNG) {
		longFetchRelease((LongFetch *) self->valuep);
	} else if (self->cdty == SQLT_CLOB || self->cdty == SQLT_BLOB
		|| self->cdty == SQLT_CFILE || self->cdty == SQLT_BFILE) {
		OCILobLocator **lobpp;
		int i;
		sword status;
		char *lobname;
		ub4 lobtype;

		SETLOBTYPE(self->cdty, lobname, lobtype);

		lobpp = (OCILobLocator **) self->valuep;
		for (i = 0; i < self->size; i++) {
			if (*lobpp != NULL)  {
				TRACE(T_HCALL,("sAs", "OCIDescriptorFree",
					*lobpp, lobname));
				status = OCIDescriptorFree((dvoid *) *lobpp,
					lobtype);
				TRACE(T_HRETURN,("sR", "OCIDescriptorFree",
					status));
				*lobpp = NULL;
				/* FIXME: Status could be OCI_INVALID_HANDLE */
			}
			lobpp++;

		}

	} else if (self->cdty == SQLT_RDD) {
		OCIRowid **rowidpp;
		int i;
		sword status;

		rowidpp = (OCIRowid **) self->valuep;
		for (i = 0; i < self->size; i++) {
			if (*rowidpp != NULL)  {
				TRACE(T_HCALL,("sAs", "OCIDescriptorFree",
					*rowidpp, "OCI_DTYPE_ROWID"));
				status = OCIDescriptorFree((dvoid *) *rowidpp,
					OCI_DTYPE_ROWID);
				TRACE(T_HRETURN,("sR", "OCIDescriptorFree",
					status));
				*rowidpp = NULL;
				/* FIXME: Status could be OCI_INVALID_HANDLE */
			}
			rowidpp++;
		}

	} else if (self->cdty == SQLT_RSET) {
		OCIStmt **stmtp;
		int i;
		sword status;

		stmtp = (OCIStmt **) self->valuep;
		for (i = 0; i < self->size; i++) {
			if (*stmtp != NULL)  {
				TRACE(T_HCALL,("ssA","OCIHandleFree",
					"OCI_HTYPE_STMT", *stmtp));
				status = OCIHandleFree(*stmtp, OCI_HTYPE_STMT);
				TRACE(T_HRETURN,("sR","OCIHandleFree",status));

				if (status != OCI_SUCCESS)
					fprintf(stderr,"OCI_ERROR %d at "
					"%s:%d\n", status,
					__FILE__, __LINE__ - 3);

				*stmtp = NULL;
				/* FIXME: Status could be OCI_INVALID_HANDLE */
			}
			stmtp++;

		}


	}

	if (self->valuep != NULL) {
		PYFREE(self->valuep);
		self->valuep = NULL;
	}

	PyObject_Del((char *) self);

	TRACE(T_EXIT,("s", "ResultSet_dealloc"));
}

/*
** ResultSet getattr
**
*/

static PyObject *ResultSet_getattr(ResultSet *self, char *name) {
	PyObject *obj;

	TRACE(T_ENTRY,("sAS", "ResultSet_getattr", self, name));

	obj = Py_FindMethod(ResultSet_methods, OBJECT(self), name);

	TRACE(T_EXIT,("sA", "ResultSet_getattr", obj));

	return obj;
}

/*
** ResultSet_length (Sequence Method)
**
** Return the length of the ResultSet
*/

static int ResultSet_length(ResultSet *self) {

	TRACE(T_ENTRY,("sA", "ResultSet_length", self));

	TRACE(T_EXIT,("sd", "ResultSet_length", self->cursor->count));

	return self->cursor->count;
}

/*
** ResultSet_concat (Sequence Method)
**
** Concatenate a resultset
*/

static PyObject *ResultSet_concat(ResultSet *self, PyObject *other) {

	TRACE(T_ERROR,("s", "ResultSet_concat"));
	PyErr_SetString(PyExc_NotImplementedError,"concat not implemented");
	return NULL;
}

/*
** ResultSet_repeat (Sequence Method)
**
** Repeat a resultset
*/

static PyObject *ResultSet_repeat(ResultSet *self, int count) {

	TRACE(T_ERROR,("s", "ResultSet_repeat"));
	PyErr_SetString(PyExc_NotImplementedError,"repeat not implemented");
	return NULL;
}

/*
** ResultSet_item (Sequence Method)
**
** Return a given value in a resultset
**
** Throws an Index Error if item is out of range
*/

static PyObject *ResultSet_item(ResultSet *self, int item) {

	ResultSetItem *rsi;


	TRACE(T_ENTRY,("sAd", "ResultSet_item", self, item));

	if (item < 0 || item >= self->cursor->count) {
		PyErr_SetString(PyExc_IndexError, "ResultSet item is out "
			"of range");
		return NULL;
	}

	if ((rsi = PyObject_NEW(ResultSetItem, &ResultSetItemType)) == NULL)
		return NULL;

	rsi->item = item;
	rsi->pyItem = NULL;
	rsi->rs = self;
	Py_INCREF(self);

	TRACE(T_EXIT,("sA", "ResultSet_item", rsi));

	return OBJECT(rsi);
}

/*
** ResultSet_slice (Sequence Method)
**
** Return a given slice in a resultset
*/

static PyObject *ResultSet_slice(ResultSet *self, int start, int stop) {

	TRACE(T_ERROR,("s", "ResultSet_slice"));
	PyErr_SetString(PyExc_NotImplementedError,"slice not implemented");
	return NULL;
}

/*
** ResultSet_ass_item (Sequence Method)
**
** assign a given item in a result set
*/

static int ResultSet_ass_item(ResultSet *self, int start, PyObject *new) {

	TRACE(T_ERROR,("s", "ResultSet_ass_item"));
	PyErr_SetString(PyExc_NotImplementedError,"item assignment not "
		"implemented");
	return -1;
}

/*
** ResultSet_ass_slice (Sequence Method)
**
** assign a given slice in a result set
*/

static PyObject *ResultSet_ass_slice(ResultSet *self, int start, int stop,
	PyObject *new) {

	TRACE(T_ERROR,("s", "ResultSet_ass_slice"));
	PyErr_SetString(PyExc_NotImplementedError,"slice assignment not "
		"implemented");
	return NULL;
}

/*
** ResultSetItem_dealloc
**
*/

static void ResultSetItem_dealloc(ResultSetItem *self) {

	TRACE(T_ENTRY,("sA", "ResultSetItem_dealloc", self));

	if (self->pyItem != NULL) {
		Py_DECREF(self->pyItem);
		self->pyItem = NULL;
	}

	Py_DECREF(self->rs);
	PyObject_Del(self);

	TRACE(T_EXIT,("s", "ResultSetItem_dealloc"));
}

/*
** ResultSetItem_getattr
**
*/

static PyObject *ResultSetItem_getattr(ResultSetItem *self, char *name) {
	PyObject *obj;

	TRACE(T_ENTRY,("sAS", "ResultSetItem_getattr", self, name));

	obj = Py_FindMethod(ResultSetItem_methods, OBJECT(self), name);

	TRACE(T_EXIT,("sA", "ResultSetItem_getattr", obj));

	return obj;
}

/*
** ResultSetItem_str
**
** Return the string representation of a ResultSetItem
*/

static PyObject *ResultSetItem_str(ResultSetItem *self) {
	ub1 *data;
	int len;
	ub2 *rlenp;
	sb2 *indp;

	TRACE(T_ENTRY,("sA", "ResultSetItem_str", self));

	data = self->rs->valuep;
	data += self->item * self->rs->width;

	rlenp = (ub2 *) self->rs->rlenp;
	rlenp += self->item;

	indp = self->rs->indp;
	indp += self->item;

	len = *rlenp;

	if (*indp == -1) {
		Py_INCREF(Py_None);
		TRACE(T_EXIT,("s", "ResultSetItem_str"));
		return Py_None;
	}

	TRACE(T_EXIT,("sSd", "ResultSetItem_str", data, len));

	return PyString_FromStringAndSize((char *) data, len);
}

/*
** ResultSetItem_value
**
** Return the string representation of a ResultSetItem
*/

static PyObject *ResultSetItem_value(ResultSetItem *self, PyObject *args) {

	int typeline;
	char buff[128];
	ub1 *data;
	int len;
	ub2 *rlenp;
	int scale;
	int precision;
	PyObject *definition;
	PyObject *field;
	PyObject *result;
	sb2 *indp;

	TRACE(T_ENTRY,("sAA", "ResultSetItem_value", self, args));

	if (self->pyItem != NULL) {
		Py_INCREF(self->pyItem);
		TRACE(T_EXIT,("sAs", "ResultSetItem_value", self->pyItem,
			"cached"));
		return self->pyItem;
	}

	data = self->rs->valuep;
	data += self->item * self->rs->width;

	rlenp = (ub2 *) self->rs->rlenp;
	rlenp += self->item;

	indp = self->rs->indp;
	indp += self->item;

	/* If the indicator is -1 (NULL) and the fetch result is
	 * OCI_SUCCESS or OCI_SUCCESS_WITH_INFO, return None
	 */
	if (*indp == -1 &&
		(self->rs->fetchResultCode==OCI_SUCCESS_WITH_INFO ||
	         self->rs->fetchResultCode==OCI_SUCCESS)) {
		Py_INCREF(Py_None);
		TRACE(T_EXIT,("sAs", "ResultSetItem_value", Py_None,
			"NULL"));
		return Py_None;
	}

	len = *rlenp;

#if ZEROLENISNULL
	if (len == 0)
		Py_INCREF(Py_None);
		TRACE(T_EXIT,("sAs", "ResultSetItem_value", Py_None,
			"0 Len"));
		return Py_None;
	}
#endif

	typeline = typeByType(self->rs->dty);

	if (typeline < 0 || TypeTable[typeline].convertout == NULL) {
		if (typeline >= 0)
			sprintf(buff,"no type converter registered for type %s",
				TypeTable[typeline].name);
		else
			sprintf(buff,"no type converter registered for unknown "
				"type %d", self->rs->dty);
		PyErr_SetString(PyExc_TypeError, buff);
		TRACE(T_ERROR,("sS", "ResultSetItem_value", buff));
		return NULL;
	}

	definition = PyList_GetItem(self->rs->definition, self->rs->column - 1);
	field = PyTuple_GetItem(definition, 4);
	scale = (int) PyInt_AsLong(field);

	field = PyTuple_GetItem(definition, 5);
	precision = (int) PyInt_AsLong(field);

	result = (*TypeTable[typeline].convertout)(self->rs->dty, len,
		scale, precision, data, self->rs->cursor->sc);

	self->pyItem = result;
	if (result != NULL)
		Py_INCREF(result);

	TRACE(T_EXIT,("sA", "ResultSetItem_value", result));

	return result;

}

/*
** _LobLocator_alloc
**
** Used by type converters as well
**
*/

static PyObject *_LobLocator_alloc(ServerContext *sc, int alloc, ub2 dty) {

	LobLocator *lob;
	sword status;
	char *lobname;
	ub4 lobtype;

	TRACE(T_ENTRY,("sAdd", "_LobLocator_alloc", sc, alloc, dty));

	if ((lob = PyObject_NEW(LobLocator, &LobLocatorType)) == NULL)
		return NULL;

	lob->sc = sc;
	Py_INCREF(lob->sc);	/* Hold on the the service context */

	lob->lobp = NULL;
	lob->errhp = NULL;
	lob->dty = dty;
	lob->ind = -1;		/* NULL indicator */

	SETLOBTYPE(dty, lobname, lobtype);

	if (alloc == 1) {

		TRACE(T_HCALL,("sAs", "OCIDescriptorAlloc", &lob->lobp,
			lobname));
		status = OCIDescriptorAlloc(sc->envhp, (dvoid **) &lob->lobp,
			lobtype, 0, NULL);
		TRACE(T_HRETURN,("sRA", "OCIDescriptorAlloc", status, lob->lobp));

		if (status != OCI_SUCCESS) {
			lob->lobp = NULL;
			Py_DECREF(lob);
			return RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		}

	}

	/* Allocate the per-locator error handle */
	TRACE(T_HCALL,("sAs","OCIHandleAlloc",&lob->errhp,
		"OCI_HTYPE_ERROR"));
	status = OCIHandleAlloc(sc->envhp, (dvoid **)&lob->errhp,
		OCI_HTYPE_ERROR, 0, NULL);
	TRACE(T_RETURN,("sRA","OCIHandleAlloc",status,lob->errhp));

	if (status != OCI_SUCCESS) {
		lob->errhp = NULL;
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(lob);
		return NULL;
	}

	TRACE(T_EXIT,("sA", "_LobLocator_alloc", lob));

	return OBJECT(lob);

}

/*
** LobLocator_alloc
**
*/

static PyObject *LobLocator_alloc(ServerContext *sc, PyObject *args) {
	PyObject *obj = NULL;
	PyObject *ctypeo = NULL;
	ub2 dty = SQLT_BLOB;
	LobLocator *lob;
	int crow = -1;

	TRACE(T_ENTRY,("sAA", "LobLocator_alloc", sc, args));

	if (!PyArg_ParseTuple(args, "|O", &ctypeo)) return NULL;

	if (ctypeo != NULL) {
		if (PyString_Check(ctypeo))
			crow = typeByName(PyString_AsString(ctypeo));
		else if (PyInt_Check(ctypeo))
			crow = typeByType((int) PyInt_AsLong(ctypeo));
		else if (PyLong_Check(ctypeo))
			crow = typeByType((int) PyLong_AsLong(ctypeo));

		if (crow == -1) {
			PyErr_SetString(PyExc_ValueError, "invalid or unknown "
				"conversion type provided");
			return NULL;
		}
		dty = TypeTable[crow].type;
	}

	lob = (LobLocator *) _LobLocator_alloc(sc, 1, dty);
	lob->dty = dty;

	obj = OBJECT(lob);

	TRACE(T_EXIT,("sAd", "LobLocator_alloc", obj, dty));

	return obj;
}

/* LobLocator_dealloc
**
*/

static void LobLocator_dealloc(LobLocator *self) {
	sword status;
#ifdef ORACLE8i
	boolean temporary = FALSE;
#endif
	char *lobname;
	ub4 lobtype;

	TRACE(T_ENTRY,("sA", "LobLocator_dealloc", self));

	SETLOBTYPE(self->dty, lobname, lobtype);

	if (self->lobp) {

#ifdef ORACLE8i
		TRACE(T_CALL,("sA", "OCILobIsTemporary", self->lobp));

		status = OCILobIsTemporary(self->sc->envhp, self->errhp,
			self->lobp, &temporary);

		TRACE(T_RETURN,("sRd", "OCILobIsTemporary", status, temporary));

		if (status != OCI_SUCCESS)
			NoticeOCIError(self->errhp, OCI_HTYPE_ERROR);

		if (temporary == TRUE) {

			TRACE(T_CALL,("sA", "OCILobFreeTemporary", self->lobp));

			status = OCILobFreeTemporary(self->sc->svchp,
				self->errhp, self->lobp );

			TRACE(T_RETURN,("sRd", "OCILobFreeTemporary", status));

			if (status != OCI_SUCCESS)
				NoticeOCIError(self->errhp, OCI_HTYPE_ERROR);
		}
#endif


		TRACE(T_HCALL,("sAs", "OCIDescriptorFree", self->lobp,
			lobname));
		status = OCIDescriptorFree(self->lobp, lobtype);
		TRACE(T_HRETURN,("sR", "OCIDescriptorFree", status));

		if (status != OCI_SUCCESS)  {
			fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
				__FILE__, __LINE__ - 3);
			NoticeOCIError(self->errhp, OCI_HTYPE_ERROR);
		}
		self->lobp = NULL;
	}

	/* Deallocate the per-locator error handle */

        if (self->errhp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_ERROR",
			self->errhp));
		status = OCIHandleFree(self->errhp, OCI_HTYPE_ERROR);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
		if (status != OCI_SUCCESS) {
                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
	}

	if (self->sc != NULL) {
		Py_DECREF(self->sc);
		self->sc = NULL;
	}

	PyObject_Del(self);

	TRACE(T_EXIT,("s", "LobLocator_dealloc"));
}

/*
** LobLocator_getattr
**
*/

static PyObject *LobLocator_getattr(LobLocator *self, char *name) {
	PyObject *obj;

	TRACE(T_ENTRY,("sAS", "LobLocator_getattr", self, name));

	obj = Py_FindMethod(LobLocator_methods, OBJECT(self), name);

	TRACE(T_EXIT,("sA", "LobLocator_getattr", obj));

	return obj;
}

/*
** _LobLocator_length
*/

static int _LobLocator_length(LobLocator *self, unsigned long *l) {

	int status;
	int rc = 0;

	TRACE(T_ENTRY,("sA", "_LobLocator_length", self));

	TRACE(T_CALL,("sA", "OCILobGetLength", self->lobp));

	Py_BEGIN_ALLOW_THREADS

	status = OCILobGetLength(self->sc->svchp, self->errhp,
		self->lobp, (ub4 *) l);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCILobGetLength", status));

	TRACE(T_RESULT,("sd", "_LobLocator_length", *l));

	if (status != OCI_SUCCESS)
		rc =  -1;

	TRACE(T_EXIT,("sR", "_LobLocator_length", rc));

	return rc;
}

/*
** LobLocator_length
**
*/


static PyObject *LobLocator_length(LobLocator *self, PyObject *args) {

	unsigned long length = 0;
	PyObject *obj;

	TRACE(T_ENTRY,("sAA", "LobLocator_length", self, args));

	if (_LobLocator_length(self, &length) != 0)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	obj = PyInt_FromLong(length);

	TRACE(T_EXIT,("sA", "LobLocator_length", obj));

	return obj;
}

/*
** LobLocator_read
**
*/

static PyObject *LobLocator_read(LobLocator *self, PyObject *args) {

	int length = -1;
	int offset = 1;
	int csid = 0;
	int csfrm = SQLCS_IMPLICIT;
	long loblength;
	PyObject *string;
	char *buff;
	sword status;

	TRACE(T_ENTRY,("sAA", "LobLocator_read", self, args));

	if (!PyArg_ParseTuple(args, "|iiii", &length, &offset, &csid, &csfrm))
		return NULL;

	if (self->ind < 0) {
		TRACE(T_EXIT,("ss","LobLocator_read","None"));
		Py_INCREF(Py_None);
		return Py_None;
	}

	TRACE(T_ARGS,("sdddd", "LobLocator_read", length, offset, csid, csfrm));

	if (_LobLocator_length(self, &loblength) != 0)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	if (length < 0 || length > loblength - offset + 1)
		length = loblength - offset + 1;

	 /* If the caller wants to read CLOB in UTF16, we have to
	prepare larger (double) buffer */
	if (csid == OCI_UTF16ID) length *= 2;

	string = PyString_FromStringAndSize(NULL, length);
	if (string == NULL) return NULL;

	if (length == 0) {	/* Short circuit a zero-length read */
		TRACE(T_EXIT,("ss","LobLocator_read","returning 0 len str"));
		return string;
	}

	buff = PyString_AsString(string);

	TRACE(T_CALL,("sAddA", "OCILobRead", self->lobp, length, offset,
		buff));
	Py_BEGIN_ALLOW_THREADS

	status = OCILobRead(self->sc->svchp, self->errhp, self->lobp,
		(ub4 *) &length, offset, buff, length, NULL, NULL, csid, csfrm);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCILobRead", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	/* return unicode string if the caller requests so */
	if (csid == OCI_UTF16ID) {
	PyObject *unicode_string = PyUnicode_FromEncodedObject(string, "utf16", NULL);
	return unicode_string;
	}


	TRACE(T_EXIT,("sA", "LobLocator_read", string));

	return string;
}

/*
** LobLocator_write
**
*/

static PyObject *LobLocator_write(LobLocator *self, PyObject *args) {

	char *s;
	int sl;
	int offset = 1;
	int csid = 0;
	int csfrm = SQLCS_IMPLICIT;
	sword status;
	PyObject *obj;
#ifdef ORACLE8i
	ub1 lobtype;
#endif


	TRACE(T_ENTRY,("sAA", "LobLocator_write", self, args));

	if (!PyArg_ParseTuple(args,"s#|iii", &s, &sl, &offset, &csid, &csfrm))
		return NULL;

	TRACE(T_ARGS,("sSdddd", "LobLocator_write", s, sl, offset, csid,
		csfrm));

#ifdef ORACLE8i
	if (self->ind < 0) {
		/* Turn this into a temporary LOB */

		if (self->dty == SQLT_CLOB)
			lobtype = OCI_TEMP_CLOB;
		else
			lobtype = OCI_TEMP_BLOB;

		TRACE(T_CALL,("sAd", "OCILobCreateTemporary", self->lobp,
			lobtype));

		status = OCILobCreateTemporary(self->sc->svchp, self->errhp,
			self->lobp, OCI_DEFAULT, OCI_DEFAULT, lobtype, FALSE,
			OCI_DURATION_SESSION);

		TRACE(T_RETURN,("sR", "OCILobCreateTemporary", status));

		if (status != OCI_SUCCESS)
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

		self->ind = 0;
	}
#endif

	TRACE(T_CALL,("sAdSd", "OCILobWrite", self->lobp, offset, s, sl));

	Py_BEGIN_ALLOW_THREADS

	/* Note that the amount is going to get garbled if the character
	** set size is not 1
	*/
	status = OCILobWrite(self->sc->svchp, self->errhp, self->lobp,
		(ub4 *) &sl, offset, s, sl, OCI_ONE_PIECE, NULL, NULL,
		csid, csfrm);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCILobWrite", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	obj = PyInt_FromLong(sl);

	TRACE(T_EXIT,("sAd", "LobLocator_write", obj, sl));

	return obj;

}

/*
** LobLocator_trim
**
*/

static PyObject *LobLocator_trim(LobLocator *self, PyObject *args) {

	PyObject *obj;
	int len;
	sword status;

	TRACE(T_ENTRY,("sAA", "LobLocator_trim", self, args));

	if (!PyArg_ParseTuple(args,"i", &len))
		return NULL;

	TRACE(T_CALL,("sAd", "OCILobTrim", self->lobp, len));

	Py_BEGIN_ALLOW_THREADS

	status = OCILobTrim(self->sc->svchp, self->errhp, self->lobp,
		len);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCILobTrim", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	obj = Py_None;
	Py_INCREF(obj);

	TRACE(T_EXIT,("s", "LobLocator_trim"));

	return obj;

}

/*
** LobLocator_open
**
*/

static PyObject *LobLocator_open(LobLocator *self, PyObject *args) {

	PyObject *obj;
	int mode = 0;
	sword status;

	TRACE(T_ENTRY,("sAA", "LobLocator_open", self, args));

	if (!PyArg_ParseTuple(args,"|i", &mode))
		return NULL;

	if (mode)
		mode = OCI_LOB_READWRITE;
	else
		mode = OCI_LOB_READONLY;

	TRACE(T_CALL,("sAd", "OCILobOpen", self->lobp, mode));

	Py_BEGIN_ALLOW_THREADS

	status = OCILobOpen(self->sc->svchp, self->errhp, self->lobp,
		mode);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCILobOpen", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	obj = Py_None;
	Py_INCREF(obj);

	TRACE(T_EXIT,("s", "LobLocator_open"));

	return obj;

}

/*
** LobLocator_getFileName
**
*/

static PyObject *LobLocator_getFileName(LobLocator *self, PyObject *args) {

	PyObject *obj;
	char dirname[32];
	ub2 ldirname = sizeof(dirname);
	char filename[256];
	ub2 lfilename = sizeof(filename);
	sword status;

	TRACE(T_ENTRY,("sAA", "LobLocator_getFileName", self, args));

	TRACE(T_INFO,("sSdSd", "LobLocator_getFileName", dirname, ldirname,
		filename, lfilename));

	TRACE(T_CALL,("sA", "OCILobFileGetName", self->lobp));

	Py_BEGIN_ALLOW_THREADS

	status = OCILobFileGetName(self->sc->envhp, self->errhp, self->lobp,
		dirname, &ldirname, filename, &lfilename);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sRSS", "OCILobFileGetName", status, dirname,
		filename));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	if ((obj = PyTuple_New(2)) == NULL) return NULL;
	PyTuple_SET_ITEM(obj, 0, PyString_FromStringAndSize(dirname, ldirname));
	PyTuple_SET_ITEM(obj, 1, PyString_FromStringAndSize(filename,
		lfilename));

	TRACE(T_EXIT,("sA", "LobLocator_getFileName", obj));

	return obj;

}

/*
** LobLocator_setFileName
**
*/

static PyObject *LobLocator_setFileName(LobLocator *self, PyObject *args) {

	PyObject *obj;
	char *dirname = NULL;
	ub2 ldirname = 0;
	char *filename = NULL;
	ub2 lfilename = 0;
	sword status;

	TRACE(T_ENTRY,("sAA", "LobLocator_setFileName", self, args));

	if (!PyArg_ParseTuple(args,"s#s#", &dirname, &ldirname, &filename,
		&lfilename)) return NULL;

	TRACE(T_CALL,("sA", "OCILobFileSetName", self->lobp));

	Py_BEGIN_ALLOW_THREADS

	status = OCILobFileSetName(self->sc->envhp, self->errhp, &self->lobp,
		dirname, ldirname, filename, lfilename);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCILobFileSetName", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	obj = Py_None;
	Py_INCREF(obj);

	TRACE(T_EXIT,("s", "LobLocator_getFileName"));

	return obj;
}

/*
** LobLocator_fileExists
**
*/

static PyObject *LobLocator_fileExists(LobLocator *self, PyObject *args) {

	PyObject *obj;
	boolean exists = 0;
	sword status;

	TRACE(T_ENTRY,("sAA", "LobLocator_fileExists", self, args));

	TRACE(T_CALL,("sA", "OCILobFileExists", self->lobp));

	Py_BEGIN_ALLOW_THREADS

	status = OCILobFileExists(self->sc->svchp, self->errhp, self->lobp,
		&exists);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sRd", "OCILobFileSetName", status, exists));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	obj = PyInt_FromLong((long) exists);

	TRACE(T_EXIT,("s", "LobLocator_getFileExists"));

	return obj;
}

/*
** LobLocator_close
**
*/

static PyObject *LobLocator_close(LobLocator *self, PyObject *args) {

	PyObject *obj;
	sword status;

	TRACE(T_ENTRY,("sAA", "LobLocator_close", self, args));

	TRACE(T_CALL,("sA", "OCILobClose", self->lobp));

	Py_BEGIN_ALLOW_THREADS

	/* Note that the amount is going to get garbled if the character
	** set size is not 1
	*/
	status = OCILobClose(self->sc->svchp, self->errhp, self->lobp);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCILobOpen", status));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	obj = Py_None;
	Py_INCREF(obj);

	TRACE(T_EXIT,("s", "LobLocator_close"));

	return obj;

}

/*
** longFetchInit
**
** Initialize an already allocated LongFetch structure
*/

static int longFetchInit(LongFetch *lf) {
	int i;

	TRACE(T_ENTRY,("sA", "longFetchInit", lf));

	if ((lf->data = (char **) PYMALLOC(FETCHBUCKETS * sizeof (char *)))
		== NULL) {

		PyErr_NoMemory();
		return 1;
	}

	if ((lf->size = (long *) PYMALLOC(FETCHBUCKETS * sizeof (long *)))
		== NULL) {
		PYFREE((char *) lf->data);
		PyErr_NoMemory();
		return 1;
	}
	if ((lf->length = (long *) PYMALLOC(FETCHBUCKETS *
		sizeof (long *))) == NULL) {
		PYFREE((char *) lf->data);
		PYFREE((char *) lf->size);
		PyErr_NoMemory();
		return 1;
	}

	for (i = 0; i < FETCHBUCKETS; i++) {
		lf->data[i] = NULL;
		lf->size[i] = 0;
		lf->length[i] = 0;
	}

	lf->volume = 0;
	lf->buckets = FETCHBUCKETS;
	lf->pieces = 0;

	TRACE(T_EXIT,("s", "longFetchInit"));

	return 0;
}

/*
** longFetchAlloc
**
** Allocate the next entry in a LongFetch stucture, growing the buckets
** if necessary
*/

static char *longFetchAlloc(LongFetch *lf, long size) {
	int bucket;
	int i;

	TRACE(T_ENTRY,("sAd", "longFetchAlloc", lf, size));

	bucket = lf->pieces;
	if (bucket >= lf->buckets) {	/* Grow buckets */
		if ((lf->data = (char **) PYREALLOC((char *) lf->data,
			(lf->buckets + FETCHBUCKETS) *
			sizeof (char *))) == NULL) {

			longFetchRelease(lf);
			PyErr_NoMemory();
			return NULL;
		}
		if ((lf->size = (long *) PYREALLOC((char *) lf->size,
			(lf->buckets + FETCHBUCKETS) *
			sizeof (long *))) == NULL) {

			longFetchRelease(lf);
			PyErr_NoMemory();
			return NULL;
		}
		if ((lf->length = (long *) PYREALLOC((char *) lf->length,
			(lf->buckets + FETCHBUCKETS) *
			sizeof (long *))) == NULL) {

			longFetchRelease(lf);
			PyErr_NoMemory();
			return NULL;
		}

		for (i = bucket; i < lf->buckets + FETCHBUCKETS; i++) {
			lf->data[i] = NULL;
			lf->size[i] = 0;
			lf->length[i] = 0;
		}

		lf->buckets += FETCHBUCKETS;
	}

	if ((lf->data[bucket] = (char *) PYMALLOC(size)) == NULL) {
		longFetchRelease(lf);
		PyErr_NoMemory();
		return NULL;
	}

	lf->size[bucket] = size;
	lf->length[bucket] = size;

	lf->pieces = bucket + 1;

	TRACE(T_EXIT,("sA", "longFetchAlloc", lf->data[bucket]));

	return lf->data[bucket];
}

/*
** longFetchRelease
**
** Release the data held by a LongFetch structure
*/

static void longFetchRelease(LongFetch *lf) {
	int i;

	TRACE(T_ENTRY,("sA", "longFetchRelease", lf));

	if (lf->data != NULL) {
		for (i = 0; i < lf->pieces; i++) {
			if (lf->data[i] != NULL) {
				PYFREE(lf->data[i]);
				lf->data[i] = NULL;
			}
		}

		PYFREE((char *) lf->data);
		lf->data = NULL;
	}

	if (lf->size != NULL) {
		PYFREE((char *) lf->size);
		lf->size = NULL;
	}

	if (lf->length != NULL) {
		PYFREE((char *) lf->length);
		lf->length = NULL;
	}

	lf->volume = 0;
	lf->buckets = 0;
	lf->pieces = 0;

	TRACE(T_EXIT,("s", "longFetchRelease"));
}

/*
** TransactionContext_alloc
**
** Makes a new transaction context from a given ServerContext
*/

static TransactionContext *TransactionContext_alloc(ServerContext *sc,
		int makehandle) {

	TransactionContext *tc = NULL;
	sword status;

	TRACE(T_ENTRY,("sA", "TransactionContext_alloc", sc));

	if ((tc = PyObject_NEW(TransactionContext, &TransactionContextType))
		== NULL) return NULL;

	tc->flags = 0;
	tc->name = NULL;
	tc->tranp = NULL;
	tc->errhp = NULL;


	if (makehandle) {
		/* Allocate the transaction handle */
		TRACE(T_HCALL,("sAs","OCIHandleAlloc",&tc->tranp,
			"OCI_HTYPE_TRANS"));
		status = OCIHandleAlloc(sc->envhp, (dvoid **)&tc->tranp,
			OCI_HTYPE_TRANS, 0, NULL);
		TRACE(T_RETURN,("sRA","OCIHandleAlloc",status,sc->errhp));

		if (status != OCI_SUCCESS) {
			tc->tranp = NULL;
			RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
			Py_DECREF(tc);
			return NULL;
		}
	}

	TRACE(T_HCALL,("sAs","OCIHandleAlloc",&tc->errhp, "OCI_HTYPE_ERROR"));
	status = OCIHandleAlloc(sc->envhp, (dvoid **)&tc->errhp,
		OCI_HTYPE_ERROR, 0, NULL);
	TRACE(T_RETURN,("sRA","OCIHandleAlloc",status,tc->errhp));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		Py_DECREF(tc);
		return NULL;
	}

	TRACE(T_EXIT,("sA", "TransactionContext_alloc", tc));

	return tc;
}

/*
** TransactionContext_dealloc
**
*/

static void TransactionContext_dealloc(TransactionContext *tc) {
	sword status;

	TRACE(T_ENTRY,("sA", "TransactionContext_dealloc", tc));

	if (tc->name) {
		Py_DECREF(tc->name);
		tc->name = NULL;
	}

        if (tc->tranp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_TRANS",
			tc->tranp));
		status = OCIHandleFree(tc->tranp, OCI_HTYPE_TRANS);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
		/*
		if (status != OCI_SUCCESS) {

                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
		*/
		tc->tranp = NULL;
	}

        if (tc->errhp) {
		TRACE(T_HCALL,("ssA","OCIHandleFree","OCI_HTYPE_ERROR",
			tc->errhp));
		status = OCIHandleFree(tc->errhp, OCI_HTYPE_ERROR);
		TRACE(T_HRETURN,("sR", "OCIHandleFree", status));
		/*
		if (status != OCI_SUCCESS) {

                        fprintf(stderr,"OCI_ERROR %d at %s:%d\n", status,
                                __FILE__,__LINE__-4);
		}
		*/
		tc->errhp = NULL;
	}

	PyObject_DEL(tc);

	TRACE(T_EXIT,("s", "TransactionContext_dealloc"));
}

/*
** TransactionContext_getXID
**
** Utility function, get the XID from the transaction handle
**
*/

static int TransactionContext_getXID(TransactionContext *tc) {
	XID *xidp = NULL;
	sword status;

	if (tc->flags & TC_XID) return 1;

	TRACE(T_HCALL,("sAsAsA", "OCIAttrGet", tc->tranp, "OCI_HTYPE_TRANS",
		&xidp, "OCI_ATTR_XID", tc->errhp));

	status = OCIAttrGet((dvoid *) tc->tranp, OCI_HTYPE_TRANS,
		&xidp, 0, OCI_ATTR_XID, tc->errhp);

	TRACE(T_HRETURN,("sR", "OCIAttrGet", status));

	if (status != OCI_SUCCESS) {
		if (status == OCI_INVALID_HANDLE)
			RaiseInvalidHandle();
		else
			RaiseOCIError(tc->errhp, OCI_HTYPE_ERROR);
		return 0;
	}

	if (xidp != NULL) {
		/* Now copy the xid */
		memcpy(&tc->xid, xidp, sizeof(tc->xid));

		tc->flags |= TC_XID;
	}

	return 1;
}

static int TransactionContext_getName(TransactionContext *tc) {
	char *name = NULL;
	sword status;

	if (tc->flags & TC_NAME) return 1;

	TRACE(T_HCALL,("sAsAsA", "OCIAttrGet", tc->tranp, "OCI_HTYPE_TRANS",
		&name, "OCI_ATTR_TRANS_NAME", tc->errhp));

	status = OCIAttrGet((dvoid *) tc->tranp, OCI_HTYPE_TRANS,
		&name, 0, OCI_ATTR_TRANS_NAME, tc->errhp);

	TRACE(T_HRETURN,("sRs", "OCIAttrGet", status, name));

	if (status != OCI_SUCCESS) {
		if (status == OCI_INVALID_HANDLE)
			RaiseInvalidHandle();
		else
			RaiseOCIError(tc->errhp, OCI_HTYPE_ERROR);
		return 0;
	}

	/* Now Make it into a string*/
	if (name != NULL) {
		tc->name = PyString_FromString(name);
		tc->flags |= TC_NAME;
	} else {
		tc->name=Py_None;
		Py_INCREF(Py_None);
	}

	return 1;
}

#define TC_GETXID(tc) if (!TransactionContext_getXID(tc)) return NULL
#define TC_GETNAME(tc) if (!TransactionContext_getName(tc)) return NULL


/*
** TransactionContext_getattr
**
*/

static PyObject *TransactionContext_getattr(TransactionContext *tc,
		char *name) {

	PyObject *value = NULL;

	TRACE(T_ENTRY,("ss", "TransactionContext_getattr", name));

	if (strncmp(name,"format",7) == 0) {
		TC_GETXID(tc);
		if (tc->flags & TC_XID)
			value = PyInt_FromLong(tc->xid.formatID);
	} else if (strncmp(name,"gtrid", 6) == 0) {
		TC_GETXID(tc);
		if (tc->flags & TC_XID)
			value = PyString_FromStringAndSize(tc->xid.data,
				tc->xid.gtrid_length);
	} else if (strncmp(name,"bqual", 6) == 0) {
		TC_GETXID(tc);
		if (tc->flags & TC_XID)
			value = PyString_FromStringAndSize(tc->xid.data +
				tc->xid.gtrid_length, tc->xid.bqual_length);
	} else if (strncmp(name,"name", 5) == 0) {
		TC_GETNAME(tc);
		value = tc->name;
		Py_INCREF(tc->name);
	} else
		value = Py_FindMethod(TransactionContext_methods,
			OBJECT(tc), name);

	if (value == NULL) {
		value = Py_None;
		Py_INCREF(Py_None);
	}

	TRACE(T_EXIT,("sA", "TransactionContext_getattr", value));
	return value;
}

/*
** TransactionContext_setName
**
** Set the name on a transaction context
*/

PyObject *TransactionContext_setName(TransactionContext *self, PyObject *args) {

	char *string = NULL;
	int slen = 0;
	sword status;

	TRACE(T_ENTRY,("sA", "TransactionContext_setName", args));

	if (!PyArg_ParseTuple(args,"s#:setName", &string, &slen)) return NULL;

	if (slen > 64) {
		PyErr_SetObject(PyExc_ValueError,Py_BuildValue("s", "size of "
			"argument too long"));
		return NULL;
	}


	TRACE(T_HCALL,("sAsAs", "OCIAttrSet", self->tranp, "OCI_HTYPE_TRANS",
		string, "OCI_ATTR_TRANS_NAME", self->errhp));

	status = OCIAttrSet((dvoid *) self->tranp, OCI_HTYPE_TRANS,
		string, slen, OCI_ATTR_TRANS_NAME, self->errhp);

	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		return NULL;
	}


	Py_XDECREF(self->name);
	self->name = PyString_FromStringAndSize(string, slen);
	self->flags |= TC_NAME;

	TRACE(T_EXIT,("s", "TransactionContext_setName"));

	Py_INCREF(Py_None);
	return Py_None;
}

/*
** TransactionContext_setXID
**
** Set the XID on a transaction context
*/

PyObject *TransactionContext_setXID(TransactionContext *self, PyObject *args) {

	int formatID = 0;
	int globalidlen = 0;
	int branchidlen = 0;
	char *globalid = NULL;
	char *branchid = NULL;

	sword status;

	TRACE(T_ENTRY,("sA", "TransactionContext_setXID", args));

	if (!PyArg_ParseTuple(args,"is#s#:setXID", &formatID,
				&globalid, &globalidlen,
				&branchid, &branchidlen)) return NULL;

	if (globalidlen > 64) {
		PyErr_SetObject(PyExc_ValueError,Py_BuildValue("s", "size of "
			"gblid too long"));
		return NULL;
	}

	if (branchidlen > 64) {
		PyErr_SetObject(PyExc_ValueError,Py_BuildValue("s", "size of "
			"bqual too long"));
		return NULL;
	}

	self->xid.formatID = formatID;
	self->xid.gtrid_length = globalidlen;
	self->xid.bqual_length = branchidlen;
	memset(self->xid.data, 0, XIDDATASIZE);
	memcpy(self->xid.data, globalid, globalidlen);
	memcpy(self->xid.data+globalidlen, branchid, branchidlen);

	TRACE(T_HCALL,("sAsAs", "OCIAttrSet", self->tranp, "OCI_HTYPE_TRANS",
		&self->xid, "OCI_ATTR_XID", self->errhp));

	status = OCIAttrSet((dvoid *) self->tranp, OCI_HTYPE_TRANS,
		&self->xid, sizeof(self->xid), OCI_ATTR_XID, self->errhp);

	TRACE(T_HRETURN,("sR", "OCIAttrSet", status));

	if (status != OCI_SUCCESS) {
		RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		return NULL;
	}

	self->flags |= TC_XID;

	TRACE(T_EXIT,("s", "TransactionContext_setXID"));

	Py_INCREF(Py_None);
	return Py_None;
}

/*
** TransactionContext_flushCache
**
** flush the XID cache
*/

PyObject *TransactionContext_flushCache(TransactionContext *self, PyObject *args) {


	TRACE(T_ENTRY,("sA", "TransactionContext_flushCache", args));

	Py_XDECREF(self->name);
	self->name = NULL;

	memset(&self->xid, 0, sizeof(self->xid));

	self->flags &= ~(TC_NAME|TC_XID);

	TRACE(T_EXIT,("s", "TransactionContext_flushCache"));

	Py_INCREF(Py_None);
	return Py_None;
}


/*
** OracleDate_alloc
**
*/

PyObject *OracleDate_alloc(PyObject *self, PyObject *args) {

	OracleDate *dateobject;
	char buf[64];
	int  ticks;
	struct tm *gmt;

	TRACE(T_ENTRY,("sA", "OracleDate_alloc", args));

	if (!PyArg_ParseTuple(args, "i", &ticks))
		/* Try the other input routine */
		return OracleDate2_alloc(self, args);

	TRACE(T_ARGS,("sd","OracleDate_alloc", ticks));

	if ((dateobject = PyObject_NEW(OracleDate, &OracleDateType)) == NULL)
		return NULL;

	/* Now turn the ticks (a time_t value) into a struct_tm */

	gmt = gmtime((const time_t *) &ticks);

	dateobject->tm.tm_sec	= gmt->tm_sec;
	dateobject->tm.tm_min	= gmt->tm_min;
	dateobject->tm.tm_hour	= gmt->tm_hour;
	dateobject->tm.tm_mday  = gmt->tm_mday;
	dateobject->tm.tm_mon	= gmt->tm_mon;
	dateobject->tm.tm_year	= gmt->tm_year;
	dateobject->tm.tm_wday	= gmt->tm_wday;
	dateobject->tm.tm_yday	= gmt->tm_yday;
	dateobject->tm.tm_isdst = gmt->tm_isdst;
#ifdef linux
	dateobject->tm.tm_gmtoff= gmt->tm_gmtoff;
	dateobject->tm.tm_zone	= gmt->tm_zone;
#endif


	sprintf(buf,"%04d-%02d-%02d %02d:%02d:%02d",
		gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
		gmt->tm_hour, gmt->tm_min, gmt->tm_sec);

	dateobject->str = PyString_FromString(buf);

	dateobject->ticks = ticks;

	sprintf(buf,"OracleDate(\"%04d-%02d-%02d %02d:%02d:%02d\")",
		gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
		gmt->tm_hour, gmt->tm_min, gmt->tm_sec);

	dateobject->repr = PyString_FromString(buf);

	/* Now fill in the oracle date internal format */

	dateobject->ocidate[0] = (gmt->tm_year + 1900) / 100 + 100;
							/* Year		*/
	dateobject->ocidate[1] = (gmt->tm_year % 100) + 100;
	dateobject->ocidate[2] = gmt->tm_mon + 1;	/* Month	*/
	dateobject->ocidate[3] = gmt->tm_mday;		/* Day		*/
	dateobject->ocidate[4] = gmt->tm_hour + 1;	/* Hour	 	*/
	dateobject->ocidate[5] = gmt->tm_min + 1;	/* Minute	*/
	dateobject->ocidate[6] = gmt->tm_sec + 1;	/* Second	*/

	/* XXX Hmm; do we need to free the gmt pointer? */

	TRACE(T_EXIT,("sA", "OracleDate_alloc", dateobject));

	return OBJECT(dateobject);
}


/*
** OracleDate_alloc
**
*/

PyObject *OracleDate2_alloc(PyObject *self, PyObject *args) {

	int year = 0;
	int month = 0;
	int day = 0;
	int hour = 0;
	int minute = 0;
	int second = 0;
	unsigned char ocidate[7];

	PyObject *result = NULL;

	PyErr_Clear();

	TRACE(T_ENTRY,("sA", "OracleDate2_alloc", args));

	if (!PyArg_ParseTuple(args, "iii|iii", &year, &month, &day,
				&hour, &minute, &second)) return NULL;

	TRACE(T_ARGS,("sdddddd","OracleDate2_alloc", year, month, day,
				hour, minute, second));


	if (  year < 0
	   || year > 9999
	   || month < 0
	   || month > 12
	   || day < 0
	   || day > 31
	   || hour < 0
	   || hour > 23
	   || minute < 0
	   || minute > 59
	   || second < 0
	   || second > 59
	   || (month < 1 && year > 0)
	   || (day < 1 && year > 0)
	   ) {
		PyErr_SetString(PyExc_ValueError,"Invalid date or time");
		return NULL;
	}

	ocidate[0] = year / 100 + 100;		/* Century	*/
	ocidate[1] = year % 100 + 100;		/* Year 	*/
	ocidate[2] = month;			/* Month	*/
	ocidate[3] = day;			/* Day		*/
	ocidate[4] = hour + 1;			/* Hour	 	*/
	ocidate[5] = minute + 1;		/* Minute	*/
	ocidate[6] = second + 1;		/* Second	*/

	result = convertOut_SQLT_DAT(SQLT_DAT, 7, 0, 0, &ocidate, NULL);

	TRACE(T_EXIT,("sA", "OracleDate2_alloc", result));

	return result;

}

/*
** OracleDate_dealloc
*/

static void OracleDate_dealloc(OracleDate *self) {

	TRACE(T_ENTRY,("sA", "OracleDate_dealloc", self));

	Py_DECREF(self->str);
	self->str = NULL;
	Py_DECREF(self->repr);
	self->repr = NULL;
	PyObject_Del(self);

	TRACE(T_EXIT,("s", "OracleDate_dealloc"));
}

/*
** OracleDate_getattr
*/

static PyObject *OracleDate_getattr(OracleDate *self, char *name) {
	PyObject *obj;

	TRACE(T_ENTRY,("sAS", "OracleDate_getattr", self, name));

	obj = Py_FindMethod(OracleDate_methods, OBJECT(self), name);

	TRACE(T_EXIT,("sA", "OracleDate_getattr", obj));

	return obj;
}

/*
** OracleDate_str
*/

static PyObject *OracleDate_str(OracleDate *self) {

	TRACE(T_ENTRY,("sA", "OracleDate_str", self));

	Py_INCREF(self->str);

	TRACE(T_EXIT,("sA", "OracleDate_str", self->str));

	return self->str;
}

/*
** OracleDate_repr
*/

static PyObject *OracleDate_repr(OracleDate *self) {
	TRACE(T_ENTRY,("sA", "OracleDate_repr", self));

	Py_INCREF(self->repr);

	TRACE(T_EXIT,("sA", "OracleDate_repr", self->repr));
	return self->repr;
}

/*
** OracleDate_cmp
**
*/

static int OracleDate_cmp(OracleDate *left, OracleDate *right) {

	int result;

	TRACE(T_ENTRY,("sAA", "OracleDate_cmp", left, right));

	result = memcmp(left->ocidate, right->ocidate, sizeof(left->ocidate));

	TRACE(T_EXIT,("sR", "OracleDate_cmp", result));

	return result;
}

/*
** OracleDate_coerce
**
** Returns 0 if two types were coerced, 1 on failure
*/

static int OracleDate_coerce(PyObject **o1, PyObject **o2) {
	int rc = 1;

	TRACE(T_ENTRY,("sAA", "OracleDate_coerce", *o1, *o2));

	if (PyInt_Check(*o2)) {
		*o1 = OracleDate_int((OracleDate *)*o1);
		rc = 0;
	} else if (PyLong_Check(*o2)) {
		*o1 = OracleDate_long((OracleDate *)*o1);
		rc =  0;
	} else if (PyFloat_Check(*o2)) {
		*o1 = OracleDate_float((OracleDate *)*o1);
		rc =  0;
	}

	if (rc == 0) Py_INCREF(*o2);

	TRACE(T_EXIT,("sR", "OracleDate_coerce", rc));

	return rc;	/* Nope */
}

/*
** OracleDate_int
*/

static PyObject *OracleDate_int(OracleDate *self) {
	PyObject *obj;

	TRACE(T_ENTRY,("sA", "OracleDate_int", self));

	obj = PyInt_FromLong(self->ticks);

	TRACE(T_EXIT,("sAd", "OracleDate_int", obj, self->ticks));

	return obj;
}

/*
** OracleDate_long
*/

static PyObject *OracleDate_long(OracleDate *self) {
	PyObject *obj;

	TRACE(T_ENTRY,("sA", "OracleDate_long", self));

	obj = PyLong_FromLong(self->ticks);

	TRACE(T_EXIT,("sAd", "OracleDate_long", self, self->ticks));
	return obj;
}

/*
** OracleDate_float
*/

static PyObject *OracleDate_float(OracleDate *self) {
	double d;
	PyObject *obj;

	TRACE(T_ENTRY,("sA", "OracleDate_float", self));
	d = (double) self->ticks;
	obj = PyFloat_FromDouble(d);

	TRACE(T_EXIT,("sAd", "OracleDate_float", obj, self->ticks));

	return obj;
}

/* --------------------------------------------------------------------
**
** Oracle Row IDs
**
** Row IDs are handled by Row ID Descriptors.  Unfortunately, as with
** LOBs, the descriptor must be allocated in advance.
*/


/*
** OracleRowID_alloc
**
** Pass in a NULL server context to not actually allocate a descriptor
*/

static PyObject *OracleRowID_alloc(ServerContext *sc) {

	OracleRowID *rid;
	sword status;

	TRACE(T_ENTRY,("sA", "OracleRowID_alloc", sc));

	if ((rid = PyObject_NEW(OracleRowID, &OracleRowIDType)) == NULL)
		return NULL;

	rid->sc = sc;
	rid->rowid = NULL;

	if (sc != NULL) {
		Py_INCREF(sc);

		TRACE(T_HCALL,("sAs", "OCIDescriptorAlloc", &rid->rowid,
			"OCI_DTYPE_ROWID"));

		status = OCIDescriptorAlloc(sc->envhp, (dvoid **) &rid->rowid,
			OCI_DTYPE_ROWID, 0, NULL);

		TRACE(T_HRETURN,("sRA", "OCIDescriptorAlloc", status,
			rid->rowid));

		if (status != OCI_SUCCESS) {
			Py_DECREF(rid);
			return RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);
		}
	}

	TRACE(T_EXIT,("sA", "OracleRowID_alloc", rid));

	return OBJECT(rid);
}

/*
** OracleRowID_dealloc
*/

static void OracleRowID_dealloc(OracleRowID *self) {

	sword status;

	TRACE(T_ENTRY,("sA", "OracleRowID_dealloc", self));


	if (self->rowid != NULL) {
		TRACE(T_HCALL,("sAs", "OCIDescriptorFree", self->rowid,
			"OCI_DTYPE_ROWID"));

		status = OCIDescriptorFree(self->rowid, OCI_DTYPE_ROWID);

		TRACE(T_HRETURN,("sR", "OCIDescriptorFree", status));

		if (status != OCI_SUCCESS)
			NoticeOCIError(self->sc->errhp, OCI_HTYPE_ERROR);

		self->rowid = NULL;
	}

	if (self->sc != NULL) {
		Py_DECREF(self->sc);
		self->sc = NULL;
	}

	PyObject_Del(self);

	TRACE(T_EXIT,("s", "OracleRowID_dealloc"));
}

/*
** OracleRowID_cmp
*/

static int OracleRowID_cmp(OracleRowID *left, OracleRowID *right) {
	int result;

	TRACE(T_ENTRY,("sAA", "OracleRowID_cmp", left, right));

	result = (left->rowid == right->rowid);

	TRACE(T_EXIT,("sR", "OracleRowID_cmp", result));

	return result;

}

/*
** OracleRowID_repr
*/

static PyObject *OracleRowID_repr(OracleRowID *self) {
	PyObject *result;

	char buff[256];

	TRACE(T_ENTRY,("sA", "OracleRowID_repr", self));

	sprintf(buff,"<OracleRowID at 0x%08x>", (unsigned int) self->rowid);

	result = PyString_FromString(buff);

	TRACE(T_EXIT,("sA", "OracleRowID_repr", result));

	return result;
}

/*
** OracleRowID_str
*/

static PyObject *OracleRowID_str(OracleRowID *self) {
	PyObject *result;

	char buff[256];

	TRACE(T_ENTRY,("sA", "OracleRowID_str", self));

	sprintf(buff,"<OracleRowID at 0x%08x>", (unsigned int) self->rowid);

	result = PyString_FromString(buff);

	TRACE(T_EXIT,("sA", "OracleRowID_str", result));

	return result;
}

/* --------------------------------------------------------------------
** Binding Arrays
**
** NOTE:
**
**	Binding arrays use malloc & co; as they have to be able to
**	operate outside of the python global interpreter lock.
** --------------------------------------------------------------------
*/

/*
** BindingArray_alloc
*/

static BindingArray *BindingArray_alloc(long entries, long itemsize) {
	BindingArray *ba;
	int i;

	TRACE(T_ENTRY, ("sdd", "BindingArray_alloc", entries, itemsize));

	entries += 1;	/* Pad by 1 for safety */

	if ((ba = (BindingArray *)malloc(sizeof (BindingArray))) == NULL)
		return NULL;

	ba->valuep	= (dvoid *) calloc(entries, itemsize);
	ba->valueszp	= (sb4 *) calloc(entries, sizeof(sb4));
	ba->dtyp	= (ub2 *) calloc(entries, sizeof(ub2));
	ba->indp	= (ub2 *) calloc(entries, sizeof(ub2));
	ba->alenp	= (ub2 *) calloc(entries, sizeof(ub2));
	ba->rcodep	= (ub2 *) calloc(entries, sizeof(ub2));
	ba->objectp	= (PyObject**) calloc(entries, sizeof(PyObject *));

	ba->itemsize	= itemsize;
	ba->count	= 0;
	ba->entries	= entries - 1;	/* Lie about our pad */

	if (ba->valuep == NULL || ba->valueszp == NULL || ba->dtyp == NULL
		|| ba->indp == NULL || ba->alenp == NULL
		|| ba->rcodep == NULL) {

		BindingArray_dealloc(ba);
		return NULL;
	}

	/* This isn't the most efficient array access... :) */
	for (i = 0; i < entries; i++) {
		ba->valueszp[i] = itemsize;
		ba->dtyp[i] = 0;
		ba->indp[i] = 0;
		ba->alenp[i] = itemsize;
		ba->rcodep[i] = 0;
	}

	TRACE(T_EXIT, ("sA", "BindingArray_alloc", ba));

	return ba;
}

/*
** BindingArray_realloc
*/

static BindingArray *BindingArray_realloc(BindingArray *ba, long entries) {
	int i;

	TRACE(T_ENTRY, ("sAd", "BindingArray_realloc", ba, entries));

	entries += 1;	/* Pad entries by 1 */

	ba->valuep	= (dvoid *) realloc(ba->valuep, ba->itemsize * entries);
	ba->valueszp	= (sb4 *) realloc(ba->valueszp, sizeof(sb4) * entries);
	ba->dtyp	= (ub2 *) realloc(ba->dtyp,     sizeof(ub2) * entries);
	ba->indp	= (ub2 *) realloc(ba->indp,     sizeof(ub2) * entries);
	ba->alenp	= (ub2 *) realloc(ba->alenp,    sizeof(ub2) * entries);
	ba->rcodep	= (ub2 *) realloc(ba->rcodep,   sizeof(ub2) * entries);
	ba->objectp	= (PyObject **) realloc(ba->rcodep,
				sizeof(PyObject *) * entries);

	i = ba->entries;
	ba->entries	= entries - 1; /* Lie about the pad */

	if (ba->valuep == NULL || ba->valueszp == NULL || ba->dtyp == NULL
		|| ba->indp == NULL || ba->alenp == NULL
		|| ba->rcodep == NULL) {

		BindingArray_dealloc(ba);
		return NULL;
	}

	for (; i < ba->entries; i++) {
		ba->valueszp[i] = ba->itemsize;
		ba->dtyp[i] = 0;
		ba->indp[i] = 0;
		ba->alenp[i] = ba->itemsize;
		ba->rcodep[i] = 0;
	}

	/* Make sure there is no garbage at the end of the realloc for
	** the object pointers */
	for (i = ba->count; i < ba->entries; i++)
		ba->objectp[i] = OBJECT(NULL);


	TRACE(T_EXIT, ("sA", "BindingArray_realloc", ba));
	return ba;
}

/*
** BindingArray_dealloc
*/

static void BindingArray_dealloc(BindingArray *ba) {
	int i;

	TRACE(T_ENTRY, ("sA", "BindingArray_dealloc", ba));

	if (ba->objectp != NULL) {
		for (i =0; i < ba->entries; i++) {
			if (ba->objectp[i] != OBJECT(NULL)) {
				Py_DECREF(ba->objectp[i]);
				ba->objectp[i] = OBJECT(NULL);
			}
		}
		free(ba->objectp);
		ba->objectp = NULL;
	}

	if (ba->valuep != NULL) {
		free(ba->valuep);
		ba->valuep = NULL;
	}

	if (ba->valueszp != NULL) {
		free(ba->valueszp);
		ba->valueszp = NULL;
	}

	if (ba->dtyp != NULL) {
		free(ba->dtyp);
		ba->dtyp = NULL;
	}

	if (ba->indp != NULL) {
		free(ba->indp);
		ba->indp = NULL;
	}

	if (ba->rcodep != NULL) {
		free(ba->rcodep);
		ba->valuep = NULL;
	}

	TRACE(T_EXIT, ("s", "BindingArray_dealloc"));
}

/*
** ServerContext_BindingArrayObject
*/

static PyObject *ServerContext_BindingArrayObject(ServerContext *self,
	PyObject *args) {

	BindingArrayObject *bao;
	PyObject *ctypeo = NULL;
	int entries = -1;
	int itemsize = -1;
	int crow = -1;
	sb2 dty = -1;

	TRACE(T_ENTRY, ("s", "ServerContext_BindingArrayObject"));

	if (!PyArg_ParseTuple(args, "|iiO", &entries, &itemsize, &ctypeo))
		return NULL;


	if (ctypeo != NULL) {
		if (PyString_Check(ctypeo))
			crow = typeByName(PyString_AsString(ctypeo));
		else if (PyInt_Check(ctypeo))
			crow = typeByType((int) PyInt_AsLong(ctypeo));
		else if (PyLong_Check(ctypeo))
			crow = typeByType((int) PyLong_AsLong(ctypeo));

		if (crow == -1) {
			PyErr_SetString(PyExc_ValueError, "invalid or unknown "
				"conversion type provided");
			return NULL;
		}
		dty = TypeTable[crow].type;
	}

	TRACE(T_ARGS, ("sddd", "ServerContext_BindingArrayObject", entries,
		itemsize, dty));

	if ((bao = PyObject_NEW(BindingArrayObject, &BindingArrayObjectType))
		== NULL) return NULL;

	Py_INCREF(self);

	bao->sc = self;

	if (entries != -1 && itemsize != -1)
		bao->ba = BindingArray_alloc(entries, itemsize);
	else
		bao->ba = NULL;	/* dynamicBindOut will try to alloc */

	bao->scale = 0;
	bao->precision = 0;

	bao->dty = dty;
	bao->flags = 0;

	TRACE(T_EXIT, ("sA", "ServerContext_BindingArrayObject", bao));

	return OBJECT(bao);
}

/*
** BindingArrayObject_dealloc
*/

static void BindingArrayObject_dealloc(BindingArrayObject *self) {

	TRACE(T_ENTRY,("sA", "BindingArrayObject_dealloc", self));

	if (self->sc != NULL) {
		Py_DECREF(self->sc);
		self->sc = NULL;
	}

	if (self->ba != NULL) {
		BindingArray_dealloc(self->ba);
		self->ba = NULL;
	}
	PyObject_Del(self);

	TRACE(T_EXIT,("s", "BindingArrayObject_dealloc"));
}

/*
** BindingArrayObject_setStatic
*/

static PyObject *BindingArrayObject_setStatic(BindingArrayObject *self,
	PyObject *args) {

	int mode = 0;
	TRACE(T_ENTRY,("sA", "BindingArrayObject_setStatic", self));

	if (!PyArg_ParseTuple(args, "i", &mode)) return NULL;

	if (mode == 0)
		self->flags &= ~BAO_STATIC;
	else
		self->flags |= BAO_STATIC;

	Py_INCREF(Py_None);

	TRACE(T_EXIT,("s", "BindingArrayObject_setStatic"));

	return Py_None;
}

/*
** BindingArrayObject_type
*/

static PyObject *BindingArrayObject_type(BindingArrayObject *self,
	PyObject *args) {

	PyObject *result;
	PyObject *ctypeo = NULL;
	int crow = -1;

	TRACE(T_ENTRY,("sAd", "BindingArrayObject_type", self, self->dty));

	if (!PyArg_ParseTuple(args, "|O", &ctypeo)) return NULL;

	result = PyInt_FromLong((long) self->dty);

	if (ctypeo != NULL) {
		if (PyString_Check(ctypeo))
			crow = typeByName(PyString_AsString(ctypeo));
		else if (PyInt_Check(ctypeo))
			crow = typeByType((int) PyInt_AsLong(ctypeo));
		else if (PyLong_Check(ctypeo))
			crow = typeByType((int) PyLong_AsLong(ctypeo));

		if (crow == -1) {
			PyErr_SetString(PyExc_ValueError, "invalid or unknown "
				"conversion type provided");
			return NULL;
		}
		self->dty = TypeTable[crow].type;
	}

	TRACE(T_EXIT,("sd", "BindingArrayObject_type", self->dty));

	return result;
}

/*
** BindingArrayObject_width
*/

static PyObject *BindingArrayObject_width(BindingArrayObject *self,
	PyObject *args) {

	PyObject *result;

	TRACE(T_ENTRY,("sA", "BindingArrayObject_width", self));

	result = PyInt_FromLong((long) self->ba->itemsize);

	TRACE(T_EXIT,("sd", "BindingArrayObject_width", self->ba->itemsize));

	return result;
}

/*
** BindingArrayObject_getattr
*/

static PyObject *BindingArrayObject_getattr(BindingArrayObject *self,
	char *name) {
	PyObject *obj;

	TRACE(T_ENTRY,("sAS", "BindingArrayObject_getattr", self, name));

	obj = Py_FindMethod(BindingArrayObject_methods, OBJECT(self), name);

	TRACE(T_EXIT,("sA", "BindingArrayObject_getattr", obj));

	return obj;
}

/*
** BindingArrayObject_length
*/

static int BindingArrayObject_length(BindingArrayObject *self) {
	int length = 0;

	TRACE(T_ENTRY,("sA", "BindingArrayObject_length", self));

	if (self->ba != NULL) length = self->ba->count;

	TRACE(T_EXIT,("sd", "BindingArrayObject_length", length));

	return length;
}

/*
** BindingArrayObject_item
*/

static PyObject *BindingArrayObject_item(BindingArrayObject *self, int item) {
	PyObject *result;
	int	typeline;
	char	buff[256];

	TRACE(T_ENTRY,("sAd", "BindingArrayObject_item", self, item));

	if (self->ba == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}

	/* Is the item out of bounds? */
	if (item < 0 || item >= self->ba->count) {
		PyErr_SetString(PyExc_IndexError,"Index out of range");
		return NULL;
	}

	/* To call a ConvertOut function, we've got to have the type,
	** length, scale, precision, data, and a server context
	**
	** we don't have scale, precision
	*/


	if (self->ba->objectp[item] != NULL) {
		result = self->ba->objectp[item];
		Py_INCREF(result);
	} else {
		/* Is the indicator none?  That would be fast */

		if (self->ba->indp[item] == -1 || self->ba->alenp[item] == 0) {
			result = Py_None;
			Py_INCREF(Py_None);
		} else {
			/* Now look up the dty in the type converter table */
			/* We have two dtys; self->dty and self->ba->dtyp[i] */
			/* Fixup datatype if not set */
			if (self->ba->dtyp[item] == 0) self->ba->dtyp[item] =
				self->dty;

			typeline = typeByType(self->ba->dtyp[item]);

			if (typeline < 0 || TypeTable[typeline].convertout ==
				NULL) {

				if (typeline >= 0)
					sprintf(buff, "no type converter "
						"registered for type %s",
						TypeTable[typeline].name);
				else
					sprintf(buff, "no type converter "
						"registered for unknown "
						"type %d",
						self->dty);
				PyErr_SetString(PyExc_TypeError, buff);
				TRACE(T_ERROR,("sS",
					"BindingArrayObject_item", buff));
				return NULL;

			}

			result = (*TypeTable[typeline].convertout)(
				self->ba->dtyp[item],
				self->ba->alenp[item],
				self->scale,		/* FIXME */
				self->precision,	/* FIXME */
				(ub1 *) self->ba->valuep +
					item*self->ba->itemsize,
				self->sc);

		}

		self->ba->objectp[item] = result;
		Py_XINCREF(result);

	}

	TRACE(T_EXIT,("sA", "BindingArrayObject_item", result));

	return result;
}

/*
** BindingArrayObject_ass_item
**
** FIXME: There is some badness here w.r.t. type conversion; normally
** bindObject is called via bindbyname or bindbypos which explicitly allow
** a conversion type parameter.  Assignment, however, does not.  Therefore,
** the current code does a bad thing, follow a tuple convention where a
** 2-tuple is used, the 2nd value being the type code.  This should be
** fixed to use the conversion type specified in the BindingArray creation.
**
*/

static int BindingArrayObject_ass_item(BindingArrayObject *bao, int item,
	PyObject *value) {

	BindingArray *ba;
	Binding mybind;
	int newcount;
	int i;
	int ctype = -1;
	int crow = -1;
	int decrefvalue = 0;
	PyObject *ctypeo = NULL;
	PyObject *bindvalue;
#ifdef NATIVENUMERIC
	OCINumber ocinumber;
	sword status;
#endif


	TRACE(T_ENTRY,("sAdA","BindingArrayObject_ass_item", bao, item, value));

	/* Allow conversion type override */

	/* XXX Inspection of Instances occurs
	** XXX instance values "type" and "value" allow for type override
	** XXX classes in DCOracle2
	*/
	if (PyInstance_Check(value)) {
		/* FIXME figure out if we needed to decref the originals */
		ctypeo = PyObject_GetAttrString(value, "type"); /* New ref */
		if (ctypeo == NULL) return -1;
		value = PyObject_GetAttrString(value, "value");	/* New ref*/
		if (value == NULL) return -1;
		decrefvalue = 1;
	}

	if (ctypeo != NULL) {
		if (PyString_Check(ctypeo))
			crow = typeByName(PyString_AsString(ctypeo));
		else if (PyInt_Check(ctypeo))
			crow = typeByType((int) PyInt_AsLong(ctypeo));
		else if (PyLong_Check(ctypeo))
			crow = typeByType((int) PyLong_AsLong(ctypeo));

		if (crow == -1) {
			PyErr_SetString(PyExc_ValueError, "invalid or unknown "
				"conversion type provided");
			return -1;
		}
		ctype = TypeTable[crow].type;

		Py_DECREF(ctypeo);		/* Release ref */
		ctypeo = NULL;
	}

	ba = bao->ba;

	/* Allow overwriting or appending the next element */
	if (item < 0 || item > ba->count + 1) {
		PyErr_SetString(PyExc_IndexError, "Illegal index for Binding "
			"Array");
		return -1;
	}

	i = item;

	if (i > ba->entries) {
		/* FIXME terrible growth strategy */
		newcount = ba->entries + 10;
		ba = BindingArray_realloc(ba, newcount);
	}

	bao->ba = ba;

	if (ba == NULL) {
		/* Raise a fatal error */
		PyErr_SetString(PyExc_MemoryError,"Out of memory growing "
			"binding array");

		return -1;
	}

	/* bindObject will try to release the prior reference */

	mybind.object = NULL;


	/*
	**  XXX *** I M P O R T A N T   C U R S O R   C O D E ***  XXX
	**
	** In order to take a PYTHON object in as a cursor, we check for
	** casts to SQLT_RSET; if this is TRUE *AND* the object has the
	** attribute _cursor, we use the _cursor attribute instead of the
	** object.  This lets dco2 inspect inside DCOracle2 cursor objects
	** to get its own cursor back.
	*/

	if (ctype == SQLT_RSET) {
		bindvalue = PyObject_GetAttrString(value, "_cursor");
		if (bindvalue == NULL) bindvalue = value;
	} else bindvalue = value;


	mybind.dty = bao->dty;

	/*
	** To avoid duplicating code, we actually call bindObject to let
	** it figure out the binding
	*/

	if (bindObject(&mybind, bindvalue, ctype) != 0) return -1;

#ifdef NATIVENUMERIC
	/*
	** NATIVE NUMERIC support will convert from original type of
	** bind to SQLT_VNU if this is a SQLT_VNU binding array
	*/

	if (bao->dty == SQLT_VNU) {
		int conversion = 1;

		OCINumberSetZero(bao->sc->errhp, &ocinumber);
		status = OCI_SUCCESS;

		if (mybind.dty == SQLT_INT) { 	/* Integer */
			status = OCINumberFromInt(bao->sc->errhp,
				mybind.valuep, mybind.valuesz,
				OCI_NUMBER_SIGNED, &ocinumber);
		} else if (mybind.dty == SQLT_FLT) { /* Float */
			status = OCINumberFromReal(bao->sc->errhp,
				mybind.valuep, mybind.valuesz,
				&ocinumber);
		} else if (mybind.dty == SQLT_STR) { /* Likely a LONG as str */
			status = OCINumberFromText(bao->sc->errhp,
				mybind.valuep, mybind.valuesz-1,
				NUMFORMAT, sizeof(NUMFORMAT)-1, NULL, 0,
				&ocinumber);
		} else {
			conversion = 0;
		}

		if (conversion) {

			if (status != OCI_SUCCESS) {
				RaiseOCIError(bao->sc->errhp, OCI_HTYPE_ERROR);
				Py_XDECREF(mybind.object);
				return -1;
			}

			Py_XDECREF(mybind.object);
			mybind.object = NULL;
			mybind.valuep = &ocinumber;
			mybind.valuesz = sizeof(ocinumber);
			mybind.alen = mybind.valuesz;
			mybind.dty = SQLT_VNU;
		}
	}
#endif

	/*
	** Then copy the data from mybind into the binding array
	*/

	if (mybind.valuesz > ba->itemsize) {
		PyErr_SetString(PyExc_OverflowError,"Assigned value too large "
			" for Binding Array");
		TRACE(T_ERROR,("ssdd", "BindingArrayObject_ass_item",
			"Overflow error", mybind.valuesz, ba->itemsize));
		Py_DECREF(mybind.object);
		if (bindvalue != value) {
			Py_DECREF(bindvalue);
		}
		return -1;
	}

	/* The zero size is returned by bindObject for cursors and/or
	** other potential pointers which need to be copied as pointers
	*/

	if (mybind.valuesz != 0)  {
		/* Copy the actual value */
		memcpy((char *) ba->valuep + (i * ba->itemsize),
			mybind.valuep, mybind.valuesz);
	} else {
		/* Just copy the pointer */
		memcpy((char *) ba->valuep + (i * ba->itemsize),
			&mybind.valuep, sizeof(&mybind.valuep));
	}

	ba->valueszp[i] = mybind.valuesz;
	ba->dtyp[i] = mybind.dty;
	ba->indp[i] = mybind.ind;
	ba->alenp[i] = mybind.alen;
	ba->rcodep[i] = mybind.rcode;

	if (ba->count < i+1)
		ba->count = i+1;

	/* Now, if the type was a CURSOR (SQLT_RSET), put the original object
	** in the binding array result; this way it won't get rebuilt on
	** refetch.
	**
	** XXX Lots of potential potholes here -- we could be saving the
	** Python layer cursor, not ours; and, do we know how dynamicBindOut
	** handles this?  Bleah.
	*/

	if (mybind.dty == SQLT_RSET || mybind.dty == SQLT_RDD) {
		if (ba->objectp[i] != NULL) {
			Py_DECREF(ba->objectp[i]);
		}
		ba->objectp[i] = value;
		Py_INCREF(value);
	} else {
		/* Toss out any saved old value object */
		Py_XDECREF(ba->objectp[i]);
		ba->objectp[i] = NULL;
	}


	Py_XDECREF(mybind.object);	/* release the object ref */


	if (ba->dtyp[i] != bao->dty) {
		TRACE(T_ERROR,("ssdd", "BindingArrayObject_ass_item",
			"Type mismatch on assignment", ba->dtyp[i], bao->dty));

		if (ba->dtyp[i] == SQLT_STR) {
			/* If it was a string, just make it whatever it is
			** supposed to be
			*/
			ba->dtyp[i] = bao->dty;
			/* Correct for trailing null */
			ba->valueszp[i] = ba->valueszp[i] - 1;
			ba->alenp[i] = ba->alenp[i] - 1;
		}

	}

	if (bindvalue != value) {
		Py_DECREF(bindvalue);
	}

	if (decrefvalue) {
		Py_DECREF(value);
	}

	TRACE(T_EXIT,("s","BindingArrayObject_ass_item"));

	return 0;

}

/* --------------------------------------------------------------------

** Conversion functions
** --------------------------------------------------------------------
*/

/*
** SQLT_CHR
**
** Just a string
*/

CONVERTOUTF(SQLT_CHR) {
	PyObject *obj;

	TRACE(T_ENTRY,("sSd", "CONVERTOUTF(SQLT_CHR)", data, len));
	obj = PyString_FromStringAndSize((char *) data, len);
	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_CHR)", obj));

	return obj;
}

/*
** SQLT_STR
**
** Just a string
*/

CONVERTOUTF(SQLT_STR) {
	PyObject *obj;

	TRACE(T_ENTRY,("sSd", "CONVERTOUTF(SQLT_STR)", data, len));
	obj = PyString_FromStringAndSize((char *) data, len);
	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_STR)", obj));

	return obj;
}

/*
** SQLT_AFC
**
** Just a string
*/

CONVERTOUTF(SQLT_AFC) {
	PyObject *obj;

	TRACE(T_ENTRY,("sSd", "CONVERTOUTF(SQLT_AFC)", data, len));
	obj = PyString_FromStringAndSize((char *) data, len);
	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_AFC)", obj));

	return obj;
}

/*
** SQLT_BIN
**
** Just binary data
*/

CONVERTOUTF(SQLT_BIN) {
	PyObject *obj;

	TRACE(T_ENTRY,("sSd", "CONVERTOUTF(SQLT_BIN)", data, len));
	obj = PyString_FromStringAndSize((char *) data, len);
	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_BIN)", obj));

	return obj;
}

/*
** SQLT_NUM
**
** We specifically request that the value come back as a string in
** The result set allocation, to make our work easier.
*/


CONVERTOUTF(SQLT_NUM) {
#ifdef NATIVENUMERIC
	return convertOut_SQLT_VNU(type, len, precision, scale, data, sc);
#else
	PyObject *result;
	double d;
	long l;


	TRACE(T_ENTRY,("sSddd", "CONVERTOUTF(SQLT_NUM)", data, len, precision,
		scale));
	/* What is the output type going to be? */

	if (scale != 0 || (precision == 0 && scale == 0 && STRICT_SCALE)) {
		sscanf((char *) data, "%lf", &d);
		result = PyFloat_FromDouble(d);
	} else if (precision < 10 || (precision == 38 && !STRICT_SCALE)) {
		sscanf((char *) data, "%ld", &l);
		result = PyInt_FromLong(l);
	} else {
		result = PyLong_FromString((char *)data, NULL, 10);
	}

	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_NUM)", result));

	return result;
#endif
}

/*
** SQLT_VNU
**
** Use the OCINumber functions
*/


CONVERTOUTF(SQLT_VNU) {
	PyObject *result;
	double d;
	long l;
	sword status;
	int bufflen;
	char buff[128];
	boolean isint;

	TRACE(T_ENTRY,("sSddd", "CONVERTOUTF(SQLT_VNU)", data, len, precision,
		scale));
	/* What is the output type going to be? */

	status =OCINumberIsInt(sc->errhp, data, &isint);
	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->errhp, OCI_HTYPE_ERROR);
		return NULL;
	}

	if (scale != 0 || (precision == 0 && scale == 0 && STRICT_SCALE) ||
		!isint) {

		status = OCINumberToReal(sc->errhp, data, sizeof(d), &d);
		result = PyFloat_FromDouble(d);
	} else if (precision < 10 || (precision == 38 && !STRICT_SCALE)) {
		status = OCINumberToInt(sc->errhp, data, sizeof(l),
			 OCI_NUMBER_SIGNED, &l);
		result = PyInt_FromLong(l);
		if (status != OCI_SUCCESS) {
			bufflen = sizeof(buff);
			status = OCINumberToText(sc->errhp, data,
				 NUMFORMATD, sizeof(NUMFORMATD)-1,
				 NULL, 0, &bufflen, buff);
			result = PyLong_FromString((char *)buff, NULL, 10);
		}
	} else {
		bufflen = sizeof(buff);
		status = OCINumberToText(sc->errhp, data,
			 NUMFORMATD, sizeof(NUMFORMATD)-1, NULL, 0,
			 &bufflen, buff);
		result = PyLong_FromString((char *)buff, NULL, 10);
	}

	if (status != OCI_SUCCESS) {
		RaiseOCIError(sc->errhp, OCI_HTYPE_ERROR);
		Py_XDECREF(result);
		return NULL;
	}

	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_VNU)", result));

	return result;
}

/*
** SQLT_INT
**
** We never ask for data back as an INT, but bindObject can create INT
** datatypes, so we need to be able to convert back
*/

CONVERTOUTF(SQLT_INT) {
	PyObject *result;
	long l;

	TRACE(T_ENTRY,("s", "CONVERTOUTF(SQLT_INT)"));

	memcpy(&l, data, sizeof(long));	/* Avoid alignment problems */
	result = PyInt_FromLong(l);

	TRACE(T_EXIT,("sdA", "CONVERTOUTF(SQLT_INT)", l, result));

	return result;
}

/*
** SQLT_FLT
**
** We never ask for data back as a FLT, but bindObject can create FLT
** datatypes, so we need to be able to convert back
*/

CONVERTOUTF(SQLT_FLT) {
	PyObject *result;
	double d;

	TRACE(T_ENTRY,("s", "CONVERTOUTF(SQLT_FLT)"));

	memcpy(&d, data, sizeof(double)); 	/* Avoid alignment probs */
	result = PyFloat_FromDouble(d);

	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_FLT)", result));

	return result;
}

/*
** SQLT_DAT
**
** We will munge the output date/time to a string.  If the underlying
** binary representation is desired the str method can retrieve it.
**
** Ideally we would instantiate a Date object to deal with this
*/
/* Following procedure added by Maan Hamze to support TIMESTAMP
 and convert it into a string 3/1/2007 */
CONVERTOUTF(SQLT_TIMESTAMP) {
	PyObject *obj;

	TRACE(T_ENTRY,("sSd", "CONVERTOUTF(SQLT_STR)", data, len));
	obj = PyString_FromStringAndSize((char *) data, len);
	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_STR)", obj));

	return obj;
}

CONVERTOUTF(SQLT_DAT) {
	OracleDate *dateobject;
	int t;
	unsigned char *c;
	unsigned char century;
	unsigned char year;
	unsigned char month;
	unsigned char day;
	unsigned char hour;
	unsigned char minute;
	unsigned char second;
	time_t bias;
	struct tm *tmbuff;
	char buf[64];

	TRACE(T_ENTRY,("sAd", "CONVERTOUTF(SQLT_DAT)", data, len));

	c = (unsigned char *) data;

	century	= *c; c++;
	year	= *c; c++;
	month	= *c; c++;
	day	= *c; c++;
	hour	= *c; c++;
	minute	= *c; c++;
	second	= *c;

	century -= 100;
	year	-= 100;
	hour	-= 1;
	minute	-= 1;
	second	-= 1;

	/* XXX Works bad for negative dates */
	t = century * 100 + year;

	if ((dateobject = PyObject_NEW(OracleDate, &OracleDateType)) == NULL)
		return NULL;

	memcpy(dateobject->ocidate,data,7);

	sprintf(buf,"%04d-%02d-%02d %02d:%02d:%02d", t, month, day, hour,
		minute, second);

	dateobject->str = PyString_FromString(buf);

	dateobject->tm.tm_sec	= second;
	dateobject->tm.tm_min	= minute;
	dateobject->tm.tm_hour	= hour;
	dateobject->tm.tm_mday  = day;
	dateobject->tm.tm_mon	= month - 1;
	dateobject->tm.tm_year	= t - 1900;
	dateobject->tm.tm_wday	= 0;
	dateobject->tm.tm_yday	= 0;
	dateobject->tm.tm_isdst = -1; /* DST unknown */
#ifdef linux
	dateobject->tm.tm_gmtoff= 0;
	dateobject->tm.tm_zone	= 0;
#endif

	if (t < 1970 || t > 2037) {
		dateobject->ticks = -1;
		bias = 0;
	} else {
		dateobject->ticks = (long) mktime(&(dateobject->tm));
		tmbuff = (struct tm *) gmtime((const time_t *)
			&dateobject->ticks);
		bias = (time_t) mktime(tmbuff) - dateobject->ticks;
	}

	if (dateobject->ticks == -1 && t == 0)
		dateobject->ticks = second + minute * 60 + hour * 3600;
	else
		dateobject->ticks -= bias;

	sprintf(buf,"OracleDate(\"%04d-%02d-%02d %02d:%02d:%02d\")",
		t, month, day, hour, minute, second);

	dateobject->repr = PyString_FromString(buf);

	TRACE(T_EXIT,("sA", "CONVERTOUTF(SQLT_DAT)", dateobject));

	return OBJECT(dateobject);

}

/*
** SQLT_LNG
**
** Long
*/

CONVERTOUTF(SQLT_LNG) {
	PyObject *string;
	char	*buf;
	LongFetch *lf = (LongFetch *) data;
	int i;

	TRACE(T_ENTRY,("sA", "CONVERTOUTF(SQLT_LNG)", data));

	/* Compute the volume of the fetch */

	for (i = 0; i < lf->pieces; i++)
		lf->volume += lf->length[i];

	string = PyString_FromStringAndSize(NULL, lf->volume);
	if (string == NULL) return NULL;

	buf = PyString_AsString(string);

	for (i = 0; i < lf->pieces; i++) {
		memcpy(buf, lf->data[i], lf->length[i]);
		buf += lf->length[i];
	}

	TRACE(T_EXIT,("sAAd", "CONVERTOUTF(SQLT_LNG)", string, buf,
		lf->volume));

	return string;
}

/*
** SQLT_LBI
**
** Long raw
*/

CONVERTOUTF(SQLT_LBI) {
	PyObject *string;
	char	*buf;
	LongFetch *lf = (LongFetch *) data;
	int i;

	TRACE(T_ENTRY,("sA", "CONVERTOUTF(SQLT_LBI)", data));

	/* Compute the volume of the fetch */

	for (i = 0; i < lf->pieces; i++)
		lf->volume += lf->length[i];

	string = PyString_FromStringAndSize(NULL, lf->volume);
	if (string == NULL) return NULL;

	buf = PyString_AsString(string);

	for (i = 0; i < lf->pieces; i++) {
		memcpy(buf, lf->data[i], lf->length[i]);
		buf += lf->length[i];
	}

	TRACE(T_EXIT,("sAAd", "CONVERTOUTF(SQLT_LBI)", string, buf,
		lf->volume));

	return string;
}


/*
** conversionFromLob
**
** Common LOB converter function
*/

static PyObject *conversionFromLob(ServerContext *sc, void *data, ub2 type) {
	OCILobLocator **ocilobpp = (OCILobLocator **) data;
	LobLocator *lob;
	sword status;
	char *lobname;
	ub4 lobtype;

	TRACE(T_ENTRY,("sAA", "conversionFromLob", sc, data));

	/* Warning -- if the pointer size allocation is goofed by the
	** output define operators, the data pointer we get may be
	** wrong (not aligned properly) re width
	*/

	if ((lob = (LobLocator *) _LobLocator_alloc(sc,0,type)) == NULL)
		return NULL;
	/* copy the lob locator POINTER */

	lob->lobp = *ocilobpp;
	lob->ind = 0;	/* dont let it be NULL!  XXX is this true? */

	SETLOBTYPE(type, lobname, lobtype);

	/* now, since the lob locator has been used, and the result set item
	** value is now NOT good to be overwritten, we must allocate a
	** new OCILobLocator for placement back in the result set
	*/

	*ocilobpp = NULL; /* Just so the descriptor doesnt try reading it */

	TRACE(T_HCALL,("sAs", "OCIDescriptorAlloc", ocilobpp, lobname));

	status = OCIDescriptorAlloc(sc->envhp, (dvoid **) ocilobpp,
		lobtype, 0, NULL);

	TRACE(T_HRETURN,("sRA", "OCIDescriptorAlloc", status, *ocilobpp));

	if (status != OCI_SUCCESS)
		return RaiseOCIError(sc->envhp, OCI_HTYPE_ENV);

	TRACE(T_EXIT,("sA", "conversionFromLob", lob));

	return OBJECT(lob);

}

/*
** SQLT_BLOB
**
** BLOB output
*/

CONVERTOUTF(SQLT_BLOB) {
	PyObject *obj;

	TRACE(T_ENTRY,("s", "CONVERTOUT(SQLT_BLOB)"));
	obj = conversionFromLob(sc, data, type);
	TRACE(T_EXIT,("sA", "CONVERTOUT(SQLT_BLOB)", obj));
	return obj;
}

/*
** SQLT_CLOB
**
** CLOB output
*/

CONVERTOUTF(SQLT_CLOB) {
	PyObject *obj;

	TRACE(T_ENTRY,("s", "CONVERTOUT(SQLT_CLOB)"));
	obj = conversionFromLob(sc, data, type);
	TRACE(T_EXIT,("sA", "CONVERTOUT(SQLT_CLOB)", obj));
	return obj;
}

/*
** SQLT_CFILE
**
** CFILE output
*/

CONVERTOUTF(SQLT_CFILE) {
	PyObject *obj;

	TRACE(T_ENTRY,("s", "CONVERTOUT(SQLT_CFILE)"));
	obj = conversionFromLob(sc, data, type);
	TRACE(T_EXIT,("sA", "CONVERTOUT(SQLT_CFILE)", obj));
	return obj;
}

/*
** SQLT_BFILE
**
** BFILE output
*/

CONVERTOUTF(SQLT_BFILE) {
	PyObject *obj;

	TRACE(T_ENTRY,("s", "CONVERTOUT(SQLT_BFILE)"));
	obj = conversionFromLob(sc, data, type);
	TRACE(T_EXIT,("sA", "CONVERTOUT(SQLT_BFILE)", obj));
	return obj;
}

/*
** SQLT_RDD
**
** Row Descriptor output
*/

CONVERTOUTF(SQLT_RDD) {
	OracleRowID *rid;
	OCIRowid *oldrid;
	OCIRowid **ocirowidpp = (OCIRowid **) data;

	TRACE(T_ENTRY,("s","CONVERTOUT(SQLT_RDD)"));

	rid = (OracleRowID *) OracleRowID_alloc(sc);

	/* copy the rowid POINTERs from the new and old */

	oldrid = rid->rowid;
	rid->rowid = *ocirowidpp;
	*ocirowidpp = oldrid;

	TRACE(T_EXIT,("sA","CONVERTOUT(SQLT_RDD)", rid));

	return OBJECT(rid);
}

/*
** SQLT_CUR
**
** Cursor result output
**
** Retrieved as SQLT_RSET
*/

CONVERTOUTF(SQLT_CUR) {
	Cursor *cur;
	OCIStmt *stmtp;
	OCIStmt **rstmt = (OCIStmt **) data;

	TRACE(T_ENTRY,("s","CONVERTOUT(SQLT_CUR)"));

	/* Create a cursor for this; the data is a REF CURSOR */

	cur = (Cursor *) ServerContext_cursor(sc, Py_None);

	/*
	** To use the resultant handle, we swap cursors; the result
	** cursor will get put in the new object, and the new cursor
	** will be put into the result binding.  The cleanup for the
	** statement will release, and the object caching for ResultSets
	** will prevent a re-swap.
	*/

	stmtp = cur->stmtp;	/* swap			*/
	cur->stmtp = *rstmt;	/*      the     	*/
	*rstmt = stmtp;		/*	    handles	*/

	/*
	** The last problem we face is doing a describe on the results
	** since we don't know what this new cursor points to
	*/

	if (Cursor_getdesc(cur) < 0) {
		Py_DECREF(cur);
		return NULL;
	}

	TRACE(T_EXIT,("sA","CONVERTOUT(SQLT_CUR)", cur));
	return OBJECT(cur);
}

/*
** SQLT_RSET
**
** Cursor result output
**
*/

CONVERTOUTF(SQLT_RSET) {
	PyObject *obj;

	TRACE(T_ENTRY,("s","CONVERTOUT(SQLT_RSET)"));

	obj = convertOut_SQLT_CUR(type, len, precision, scale, data, sc);

	TRACE(T_EXIT,("sA","CONVERTOUT(SQLT_RSET)", obj));

	return obj;
}


/*
** --------------------------------------------------------------------
**
** Number conversion
**
** --------------------------------------------------------------------
*/

static PyObject *numberConversion(char *repr) {

	/* FIXME to do actual number conversion */

	Py_INCREF(Py_None);
	return Py_None;
}

/*
** --------------------------------------------------------------------
** Trace support -- returns the address of the trace code for the
** trace macro
** --------------------------------------------------------------------
*/

char *trace(char *fmt, ...) {
	va_list ap;
	char *s;
	int d;
	int i = 0;
	traceRecord *tr;
	static long last_thread = -1;

	if (traceBase == NULL) return NULL; /* FIXME -- macro cant take */

	if (traceFlags & T_TSWITCH) {
		long tid = PyThread_get_thread_ident();
		if (tid != last_thread) {
			last_thread = tid;
			TRACE(T_TSWITCH,("sd", "ThreadSwitch", tid));
		}
	}

	if (traceNext >= traceCount) traceNext = 0;
	tr = traceBase + traceNext++;

	tr->code = 0xff;

	gettimeofday(&tr->tv, NULL);

	/* Zero prior record */
	for (i = 0; i < 7; i++) {
		tr->fmt[i] = '\0';
		tr->args[i].s = NULL;
	}

	i = 0;

	va_start(ap, fmt);
	while (*fmt && i < 7) {
		switch(*fmt) {
			case 'R':		/* Result	*/
			case 'd':		/* decimal	*/
				d = va_arg(ap, int);
				tr->args[i].i = d;
				break;
			case 'A':		/* Address	*/
			case 'S':		/* volatile string */
			case 's':		/* string */
				s = va_arg(ap, char *);
				tr->args[i].s = s;
				break;
		}
		tr->fmt[i] = *fmt;

		i++;
		fmt++;
	}
	va_end(ap);

	return &tr->code;
}

/*
** Traceback
*/

static PyObject *Traceback(PyObject *self, PyObject *args) {

	PyObject *list;
	PyObject *rec;
	PyObject *item;
	unsigned int t;
	traceRecord *tr;
	int i;
	char buff[4096];
	double d;

	if (traceBase == NULL) {
		PyErr_SetString(PyExc_NotImplementedError, "Trace table not "
		"available");
		return NULL;
	}

	if ((list = Py_BuildValue("[]")) == NULL)
		return NULL;

	t = (traceNext + 1) % traceCount;

	if (traceBase[traceCount -1].code == 0x00) t = 0;

	while (t != traceNext) {
		tr = traceBase + t;

		t = (t + 1) % traceCount;

		if (tr->code == 0x00 || tr->code == 0xff) continue;

		if ((rec = Py_BuildValue("[]")) == NULL)
			return NULL;

		d = tr->tv.tv_sec;
		d += (double) tr->tv.tv_usec / 1000000.0;

		item = PyFloat_FromDouble(d);
		PyList_Append(rec, item);
		Py_DECREF(item);

		item = PyInt_FromLong((long) tr->code);
		PyList_Append(rec, item);
		Py_DECREF(item);

		for (i = 0; i < 7; i++) {
			if (tr->fmt[i] == '\0') break;
			switch (tr->fmt[i]) {
				case 'A':
					sprintf(buff, "0x%08lx",
						(unsigned long)tr->args[i].s);
					item = PyString_FromString(buff);
					break;
				case 'S':
					sprintf(buff, "0x%08lx ?= %.20s",
						(unsigned long)tr->args[i].s,
						tr->args[i].s != NULL ?
						tr->args[i].s : "(null)");
					item = PyString_FromString(buff);
					break;
				case 's':
					item = PyString_FromString(
						tr->args[i].s);
					break;
				case 'R':
				case 'd':
					item = PyInt_FromLong(
						(long)tr->args[i].i);
					break;
			}
			PyList_Append(rec,item);
			Py_DECREF(item);

		}
		PyList_Append(list, rec);
		Py_DECREF(rec);
	}

	return list;
}

/*
** Traceprint1
**
** Used by Traceprint() and for dump file creation
**
*/

static void Traceprint1(FILE *file, int count) {

	unsigned int t;
	traceRecord *tr;
	int i;
	int len;
	double d;

	if (file == NULL) return; /* For TRACE macro no trace file */

	if (traceBase == NULL) {
		fprintf(file, "Trace table not available\n");
		return;
	}


	t = (traceNext + 1) % traceCount;

	/* Negative counts mean, just print the absval from the end */
	/* XXX Possible bad things happen if count < -traceCount    */

	if (count < 0) {
		t = (traceNext + count + traceCount) % traceCount;
		count = -count;
	} else if (traceBase[traceCount -1].code == 0x00) t = 0;

	if (count == 0) count = traceCount + 1;


	while (t != traceNext && count > 0) {
		tr = traceBase + t;
		count--;

		t = (t + 1) % traceCount;

		if (tr->code == 0x00 || tr->code == 0xff) continue;

		d = tr->tv.tv_sec;
		d += (double) tr->tv.tv_usec / 1000000.0;

		fprintf(file,"* %12.3f, %3d, ", d, tr->code);

		for (i = 0; i < 7; i++) {
			if (tr->fmt[i] == '\0') break;
			switch (tr->fmt[i]) {
				case 'A':
					fprintf(file, "0x%08lx, ",
						(unsigned long)tr->args[i].s);

					break;
				case 'S':
					/* Go for a page */
					len = 4097 - (unsigned long)
						tr->args[i].s % 4096;
					if (len < 20) len = 20; /* XXX */
					fprintf(file, "0x%08lx ?= %.*s, ",
						(unsigned long)tr->args[i].s,
						len,
						tr->args[i].s != NULL ?
						tr->args[i].s : "(null)");
					break;
				case 's':
					fprintf(file,"%s, ", tr->args[i].s);
					break;
				case 'R':
				case 'd':
					fprintf(file,"%d, ",tr->args[i].i);
					break;
			}

		}
		fprintf(file,"*\n");
	}
	fflush(file);
}

/*
** Traceprint will dump the trace table to stderr
*/

UNUSED static void Traceprint(void) {
	Traceprint1(stderr, 0);
}

/*
** Tracedump will dump the trace table to a file
*/

static void Tracedump1() {
	FILE *file;

	if (traceDump == NULL) return;

	if ((file = fopen(traceDump,"a")) == NULL) file = stderr;

	fprintf(file,"-----> dco2 Tracedump <-----\n");

	Traceprint1(file, 0);

	if (file != stderr) fclose(file);
}

/*
** For debugging
*/

UNUSED static void Tracedump(void) {
	if (traceDump == NULL) traceDump = "dco2.tdm";
	Tracedump1();

}


#ifdef DCO2MEMTRACE
/*
** Memory allocator debug functions
**
*/

/*
** dco2_pymem_malloc
**
*/

static void *dco2_pymem_malloc(unsigned int size) {
	void *ptr;

	ptr = PyMem_Malloc(size);

	TRACE(T_MALLOC,("sdA", "PyMem_Malloc", size, ptr));

	return ptr;
}

static void *dco2_pymem_realloc(void *ptr, unsigned int size) {
	void *new;

	new = PyMem_Realloc(ptr, size);

	TRACE(T_REALLOC,("sdAA", "PyMem_Realloc", size, ptr, new));

	return new;
}

static void dco2_pymem_free(void *ptr) {

	TRACE(T_FREE,("sA", "PyMem_Free", ptr));

	PyMem_Free(ptr);
}
#endif

#ifdef WIN32

/*
** --------------------------------------------------------------------
** Compatibility functions
** --------------------------------------------------------------------
*/

static void gettimeofday(struct timeval *tv, void *zone) {
#ifdef USEFILETIME
	FILETIME filetime;
	GetSystemTimeAsFileTime(&filetime);
	tv->tv_usec = filetime.dwLowDateTime;
	tv->tv_sec  = filetime.dwHighDateTime;
#else
	tv->tv_sec = clock();
	tv->tv_usec = 0;
#endif
}

#endif
/*
** --------------------------------------------------------------------
** Module initialization
** --------------------------------------------------------------------
*/

#ifdef WIN32                             //Added by Maan Hamze 3/2007
__declspec (dllexport)
#endif
PUBLIC void initdco2(void) {
	PyObject *module;
	PyObject *dict;
	char *tracefilename = NULL;
	char *traceflags;
	char *tracesize = NULL;
	char *rev = "$Revision: 1.136 $";
	PyObject *eo;


	if ((traceflags = getenv("DCO2TRACEFLAGS")) != NULL) {
		traceFlags = atoi(traceflags);
		traceCount = TRACECOUNT;
	}

	/* XXX this could be used to clobber files that shouldnt be */
	if ((tracefilename = getenv("DCO2TRACELOG")) != NULL) {

		traceLog = fopen(tracefilename,"a");
		if (traceLog == NULL) perror(tracefilename);
		traceCount = TRACECOUNT;
	}

	/* XXX ditto */
	traceDump = getenv("DCO2TRACEDUMP");
	if (traceDump != NULL) traceCount = TRACECOUNT;

	if ((tracesize = getenv("DCO2TRACESIZE")) != NULL) {
		traceCount = atoi(tracesize);
		if (traceCount == 0) traceFlags = 0;
	}

	/* Be on the lookout for alignment problems with trace records
	** on some platforms.  Should be OK but...
	*/

	if (traceCount > 0) {

		traceBase = (traceRecord *)
			calloc(traceCount, sizeof(traceRecord));
	} else traceFlags = 0;


	TRACE(T_ENTRY, ("sAd","initdco2",initdco2,traceFlags));

	module = Py_InitModule("dco2", dco2_methods);

	dict = PyModule_GetDict(module);

	ServerContextType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "ServerContextType",
		OBJECT(&ServerContextType));

	TransactionContextType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "TransactionContextType",
		OBJECT(&TransactionContextType));

	LobLocatorType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "LobLocatorType",
		OBJECT(&LobLocatorType));

	CursorType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "CursorType",
		OBJECT(&CursorType));

	ResultSetType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "ResultSetType",
		OBJECT(&ResultSetType));

	ResultSetItemType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "ResultSetItemType",
		OBJECT(&ResultSetItemType));

	OracleDateType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "OracleDateType",
		OBJECT(&OracleDateType));

	OracleRowIDType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "OracleRowIDType",
		OBJECT(&OracleRowIDType));

	BindingArrayObjectType.ob_type = &PyType_Type;
	PyDict_SetItemString(dict, "BindingArrayObjectType",
		OBJECT(&BindingArrayObjectType));

	/* Errors
	**
	** TODO: save all the error objects so we can refer to them later
	** if need be; right now we only save the DatabaseError object
	*/

	eo = PyErr_NewException("dco2.Warning", PyExc_StandardError,
		NULL);
	PyDict_SetItemString(dict, "Warning", eo);
	Py_DECREF(eo);


	if (ErrorObject == NULL)
		ErrorObject = PyErr_NewException("dco2.Error",
			PyExc_StandardError, NULL);
	PyDict_SetItemString(dict, "Error", ErrorObject);


	eo = PyErr_NewException("dco2.InterfaceError", ErrorObject,
		NULL);
	PyDict_SetItemString(dict, "InterfaceError", eo);
	Py_DECREF(eo);


	eo = PyErr_NewException("dco2.DatabaseError", ErrorObject,
		NULL);
	PyDict_SetItemString(dict, "DatabaseError", eo);

	Py_DECREF(ErrorObject);
	ErrorObject = eo;

	eo = PyErr_NewException("dco2.InternalError", ErrorObject,
		NULL);
	PyDict_SetItemString(dict, "InternalError", eo);
	Py_DECREF(eo);

	eo = PyErr_NewException("dco2.OperationalError", ErrorObject,
		NULL);
	PyDict_SetItemString(dict, "OperationalError", eo);
	Py_DECREF(eo);

	if (ProgrammingErrorObject == NULL)
		ProgrammingErrorObject =
			PyErr_NewException("dco2.ProgrammingError",
			ErrorObject, NULL);
	PyDict_SetItemString(dict, "ProgrammingError", ProgrammingErrorObject);

	eo = PyErr_NewException("dco2.IntegrityError", ErrorObject,
		NULL);
	PyDict_SetItemString(dict, "IntegrityError", eo);
	Py_DECREF(eo);

	eo = PyErr_NewException("dco2.DataError", ErrorObject,
		NULL);
	PyDict_SetItemString(dict, "DataError", eo);
	Py_DECREF(eo);

	eo = PyErr_NewException("dco2.NotSupportedError", ErrorObject,
		NULL);
	PyDict_SetItemString(dict, "NotSupportedError", eo);
	Py_DECREF(eo);

	PyDict_SetItemString(dict, "__version__",
		PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));

	/* Record some of our build parameters */
	PyDict_SetItemString(dict, "buildDefs", PyString_FromString(
		""
#ifdef ORACLE8i
		"-DORACLE8i "
#endif
#ifdef ORACLE9i
		"-DORACLE9i "
#endif
#ifdef USEOBJECT
		"-DUSEOBJECT "
#endif
#ifdef linux
		"-Dlinux "
#endif
#ifdef WIN32
		"-DWIN32 "
# ifdef USEFILETIME
		"-DUSEFILETIME "
# endif
#endif
#ifdef DCO2MEMTRACE
		"-DDCO2MEMTRACE "
#endif
#if NATIVENUMERIC
		"-DNATIVENUMERIC=1 "
#endif
#ifdef COMMONENVIRONMENT
		"-DCOMMONENVIRONMENT "
#endif
#ifdef _REENTRANT
		"-D_REENTRANT "
#endif
#if STRICT_SCALE
		"-DSTRICT_SCALE=1 "
#endif
	));


#if 0
	if (PyErr_Occurred())
		Py_FatalError("Can't initialize module dco2");

	fprintf(stderr,"ServerContext size: %d\n", sizeof(ServerContext));
	fprintf(stderr,"Binding size: %d\n", sizeof(Binding));
	fprintf(stderr,"BindingArray size: %d\n", sizeof(BindingArray));
	fprintf(stderr,"Cursor size: %d\n", sizeof(Cursor));
	fprintf(stderr,"ResultSet size: %d\n", sizeof(ResultSet));
#endif

	TRACE(T_EXIT, ("sR","initdco2",0));
}
